• 技术文章 >Java >java教程

    Java经典技巧之实现多线程、线程同步

    长期闲置长期闲置2022-04-26 18:05:36转载694
    本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于核心类库中多线程和线程同步的相关问题,下面就一起来看一下,希望对大家有帮助。

    推荐学习:《java视频教程

    1 多线程

    1.1 进程

    1.2 线程

    1.3 多线程的实现方式

    1.3.1 方式1:继承Tread类

    package test;//1、定义一类MyTread继承Tread类public class MyThread extends Thread{
        2、在MyTread类中重写run()方法
        @Override
        public void run() {
            for(int i=0;i<100;i++) {
                System.out.println(i);
            }
        }}
    package test;public class Demo {
        public static void main(String[] args) {
            //3、创建MyTread类的对象
            MyThread my1 = new MyThread();
            MyThread my2 = new MyThread();
    
            //4、启动线程:void start():启动线程,由Java虚拟机调用此线程的run()方法
            my1.start();
            my2.start();
        }}

    1.3.2 方式2:实现Runnable接口

    package test;public class Demo {
        public static void main(String[] args) {
            //3、创建MyRunnable类的对象
            MyRunnable mr = new MyRunnable();
    
            //4、创建Tread类的对象,把MyRunnable对象作为构造方法的参数//        Thread t1 = new Thread(mr);//        Thread t2 = new Thread(mr);
            //Thread(Runnable target,String name)
            Thread t1 = new Thread(mr,"高铁");
            Thread t2 = new Thread(mr,"飞机");
    
            //5、启动线程
            t1.start();
            t2.start();
        }}

    1.3.3 方式3:实现Callable接口

    1.4 设置和获取线程名称

    方法名说明
    void setName(Stringname)将此线程的名称更改为等于参数name
    String getName()返回此线程的名称
    public Thread(String name)通过构造方法也可以设置线程名称
    public static Thread currentThread()返回对当前正在执行的线程对象的引用(可以返回main()方法中线程)
    public static void sleep(long time)让当前线程休眠多少毫秒再继续执行
    package test;public class MyThread extends Thread{
        //构造方法添加线程名称
        public MyThread(){}
        public MyThread(String name) {
            super(name);
        }
        
        @Override
        public void run() {
            for(int i=0;i<100;i++) {
                //1,String getName() 	返回此线程的名称
                System.out.println(getName()+":"+i);
            }
        }}
    package test;public class Demo {
        public static void main(String[] args) {
            /*        MyThread my1 = new MyThread();
            MyThread my2 = new MyThread();
    
            //2,void setName(Stringname) 	将此线程的名称更改为等于参数name
            my1.setName("高铁");
            my2.setName("飞机");*/
            
            //3,通过构造方法设置线程名称
            //需要自己定义的类中提供此带参构造方法,并通过super访问父类带参构造方法
            /*MyThread my1 = new MyThread("高铁");
            MyThread my2 = new MyThread("飞机"); 
    
            my1.start();
            my2.start();*/
            
            //4,public static Thread currentThread() 	返回对当前正在执行的线程对象的引用(可以返回main()方法中线程)
            System.out.println(Tread.currentThread().getName()); //main
        }}

    1.5 线程调度

    方法名说明
    public final int getPriority() [praɪˈɔːrəti]返回此线程的优先级
    public final void setPriority(int newPriority)更改此线程的优先级
    package test;public class Demo {
        public static void main(String[] args) {
    
            ThreadPriority tp1 = new ThreadPriority();
            ThreadPriority tp2 = new ThreadPriority();
            ThreadPriority tp3 = new ThreadPriority();
    
    
            tp1.setName("高铁");
            tp2.setName("飞机");
            tp3.setName("汽车");
    
            //1,public final int getPriority() [praɪˈɔːrəti] 	返回此线程的优先级//        System.out.println(tp1.getPriority()); //5//        System.out.println(tp2.getPriority()); //5//        System.out.println(tp3.getPriority()); //5
    
            //2,public final void setPriority(int newPriority) 	更改此线程的优先级
            System.out.println(Thread.MAX_PRIORITY); //10
            System.out.println(Thread.MIN_PRIORITY); //1
            System.out.println(Thread.NORM_PRIORITY); //5
    
            //设置正确优先级
            tp1.setPriority(5);
            tp2.setPriority(10);
            tp3.setPriority(1);
    
            tp1.start();
            tp2.start();
            tp3.start();
        }}

    1.6 线程控制

    方法名说明
    static void sleep(long millis)使当前正在执行的线程停留(暂停执行)指定的毫秒数
    void join()等待这个线程死亡
    void setDaemon(boolean on) [ˈdiːmən]将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机很快将退出 (并不是立刻退出)

    案例:sleep()方法

    package test;public class ThreadSleep extends Thread{
        @Override
        public void run() {
            for(int i=0;i<10;i++) {
                System.out.println(getName()+":"+i);
                //1,static void sleep(long millis) 	使当前正在执行的线程停留(暂停执行)指定的毫秒数
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }}
    package test;public class Demo {
        public static void main(String[] args) {
    
            ThreadSleep ts1 = new ThreadSleep();
            ThreadSleep ts2 = new ThreadSleep();
            ThreadSleep ts3 = new ThreadSleep();
    
    
            ts1.setName("曹操");
            ts2.setName("刘备");
            ts3.setName("孙权");
    
    
            ts1.start();
            ts2.start();
            ts3.start();//        曹操:0//        孙权:0//        刘备:0//        孙权:1//        曹操:1//        刘备:1//        ...
        }}

    案例:join()方法

    package test;public class Demo {
        public static void main(String[] args) {
    
            ThreadJoin tj1 = new ThreadJoin();
            ThreadJoin tj2 = new ThreadJoin();
            ThreadJoin tj3 = new ThreadJoin();
    
            tj1.setName("康熙");
            tj2.setName("四阿哥");
            tj3.setName("八阿哥");
    
            tj1.start();
            //2,void join() 	等待这个线程死亡
            try {
                tj1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tj2.start();
            tj3.start();//        康熙:0//        康熙:1//        康熙:2//        四阿哥:0//        四阿哥:1//        八阿哥:0//        八阿哥:1//        八阿哥:2//        四阿哥:2//        ...
        }}

    案例:setDaemon()方法

    package test;public class Demo {
        public static void main(String[] args) {
    
            ThreadJoin tj1 = new ThreadJoin();
            ThreadJoin tj2 = new ThreadJoin();
            ThreadJoin tj3 = new ThreadJoin();
    
            tj2.setName("关羽");
            tj3.setName("张飞");
            //设置主线程为刘备
            Thread.currentThread().setName("刘备");
    
            //3,void setDaemon(boolean on) 	将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
            tj1.setDaemon(true);
            tj2.setDaemon(true);
    
            tj1.start();
            tj2.start();
    
            for(int i=0;i<2;i++) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
            //刘备执行完后,关羽和张飞会很快结束
        }}

    1.7 线程生命周期

    在这里插入图片描述

    1.8 数据安全问题之案例:买票

    在这里插入图片描述
    在这里插入图片描述

    1.9 线程同步_同步代码块

    synchronized(任意对象) {
    	多条语句操作共享数据的代码}
    package test;//1,定义一个类SellTicket实现Runnable接口,里面定义一个成员变量: private int tickets= 100;public class SellTicket implements Runnable{
        private int tickets = 100;
        private Object obj = new Object();
    
        //2,在ellTicket类中重写run0方法实现卖票, 代码步骤如下
        @Override
        public void run() {
            while(true) {
                //tickes=100
                //t1,t2,t3
                //假设t1抢到CPU执行器
                synchronized (obj){
                    //t1进来后把代码锁起来了
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                            //t1休息100毫秒
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //窗口1正在出售第100张票
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                        tickets--; //tickets=99
                    }
                    //t1出来了,锁就被释放了
                }
            }
        }}
    package test;public class SellTicketDemo {
        public static void main(String[] args) {
            //创建SellTicket类的对象
            SellTicket st = new SellTicket();
    
            //创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称
            Thread t1 = new Thread(st,"窗口1");
            Thread t2 = new Thread(st,"窗口2");
            Thread t3 = new Thread(st,"窗口3");
    
            //启动线程
            t1.start();
            t2.start();
            t3.start();
    
        }}

    1.10 线程同步_同步方法

    package test;public class SellTicket implements Runnable{//1非静态    private int tickets = 100;
        private static int tickets = 100;
        private Object obj = new Object();
        private int x = 0;
    
        @Override
        public void run() {
            while(true) {
                if(x%2==0) {//1非静态                synchronized (this) {
                    synchronized (SellTicket.class) {
                        if (tickets > 0) {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                            tickets--; //tickets=99
                        }
                    }
                } else {//                synchronized (obj) {//                    if (tickets > 0) {//                        try {//                            Thread.sleep(100);//                        } catch (InterruptedException e) {//                            e.printStackTrace();//                        }//                        System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");//                        tickets--; //tickets=99//                    }//                }
                    sellTicket();
                }
                x++;
            }
        }//1非静态//    private synchronized void sellTicket() {//        if (tickets > 0) {//            try {//                Thread.sleep(100);//            } catch (InterruptedException e) {//                e.printStackTrace();//            }//            System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");//            tickets--; //tickets=99//        }//    }
    
        private static synchronized void sellTicket() {
            if (tickets > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                tickets--; //tickets=99
            }
        }}

    1.11 线程安全的类(了解)

    源码中方法都被synchronized修饰

    StringBuffer

    Vector

    Hashtable

    Collections类中static <T> List<T> snchronizedList(List<T> list):返回由指定列表支持的同步(线程安全)的列表

    package test;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;public class Demo {
        public static void main(String[] args)  {
            //static <T> List<T> snchronizedList(List<T> list):返回由指定列表支持的同步(线程安全)的列表
            Collection<String> list = Collections.synchronizedList(new ArrayList<String>());
    
            /*源码都是返回Synchronized
            public static <T> List<T> synchronizedList(List<T> list) {
                return (list instanceof RandomAccess ?
                        new Collections.SynchronizedRandomAccessList<>(list) :
                        new Collections.SynchronizedList<>(list));
            }*/
    
        }}

    1.12 Lock锁

    方法名说明
    ReentrantLock()创建一个ReentrantLock的实例对象
    方法名说明
    void lock()获得锁
    void unlock()释放锁
    package test;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SellTicket implements Runnable{
        private static int tickets = 100;
        private Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while(true) {
                try {
                    lock.lock();
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                        tickets--;
                    }
                }finally {
                    lock.unlock();
                }
            }
        }}

    1.13 线程通讯

    方法名说明
    public void wait()让当前线程进入到等待状态,此方法必须锁对象调用
    public void notify()唤醒当前锁对象上等待状态的某个线程,此方法必须锁对象调用
    public void notifyAll()唤醒当前锁对象上等待状态的全部线程,此方法必须锁对象调用

    1.14 生产者消费者

    1.14.1 生产者消费者概述

    在这里插入图片描述

    方法名说明
    void wait()导致当前线程等待,直到另一个线程调用该对象的 notify() 方法或 notifyAll() 方法
    void notify()唤醒正在等待对象监视器的单个线程
    void notifyAll()唤醒正在等待对象监视器的所有线程

    1.14.2 生产者消费者案例

    在这里插入图片描述

    package test;//1:定义奶箱类public class Box {
        //定义一个成员变量,表示第x瓶奶
        private int milk;
        //定义一个成员变量表示奶箱的状态
        private boolean state = false;
    
        //提供存储牛奶和获取牛奶的操作
        public  synchronized void put(int milk) {
            //如果有牛奶等待消费
            if(state) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            //如果没有牛奶,就生产牛奶
            this.milk = milk;
            System.out.println("送奶工将第" + this.milk + "瓶奶放入奶箱");
    
            //生产完毕后,修改奶箱状态
            state = true;
    
            //唤醒其他等待线程
            notifyAll();
        }
    
        public  synchronized void get() {
            //如果没有牛奶,就等到生产
            if(!state) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //如果有牛奶,就消费牛奶
            System.out.println("用户拿到第" + this.milk + "瓶奶");
    
            //消费完毕后,修改奶箱状态
            state = false;
    
            //唤醒其他等待线程
            notifyAll();
        }}
    package test;//2:生产者类(Producer):实现Runnable接口public class Producer implements Runnable {
        private Box b;
    
        public Producer(Box b) {
            this.b = b;
        }
    
        //重写run()方法,调用存储牛奶的操作
        @Override
        public void run() {
            for (int i = 1; i <= 5; i++) {
                b.put(i);
            }
        }}
    package test;//3:消费者类(Customer);实现Runnable接口public class Customer implements Runnable{
        private Box b;
    
        public Customer(Box b) {
            this.b = b;
        }
    
        //重写run()方法,调用获取牛奶的操作
        @Override
        public void run() {
            while(true) {
                b.get();
            }
        }}
    package test;public class BoxDemo {
        public static void main(String[] args) {
            //创建奶箱对象,这是共享数据区域
            Box b = new Box();
    
            //创建生产者对象,把奶箱对象作为构造方法参数传递。因为在这个类中要谓用存储牛奶的操作
            Producer p = new Producer(b);
    
            //创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
            Customer c  =new Customer(b);
    
            //创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
            Thread t1 = new Thread(p);
            Thread t2 = new Thread(c);
    
            //启动线程
            t1.start();
            t2.start();//        送奶工将第1瓶奶放入奶箱//                用户拿到第1瓶奶//        送奶工将第2瓶奶放入奶箱//                用户拿到第2瓶奶//        送奶工将第3瓶奶放入奶箱//                用户拿到第3瓶奶//        送奶工将第4瓶奶放入奶箱//                用户拿到第4瓶奶//        送奶工将第5瓶奶放入奶箱//                用户拿到第5瓶奶
        }}

    推荐学习:《java视频教程

    以上就是Java经典技巧之实现多线程、线程同步的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:CSDN,如有侵犯,请联系admin@php.cn删除
    专题推荐:java
    上一篇:Java归纳总结之数组详解 下一篇:Java设计模式解析之适配器模式(实例详解)
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• 实例详解JAVA抽象工厂模式• JavaScript实例详解之旋转数组• Java实例详解之Lambda表达式• Java归纳总结之数组详解• 完全掌握JavaScript运行机制及原理
    1/1

    PHP中文网