技术

Java多线程编程核心技术

2019-04-26  本文已影响14人  LeonardoEzio

本文主要基于 高洪岩《Java多线程编程核心技术》一书,文章目录基本与书保持一致,由于想偷懒就将看书过程中的md笔记拿来直接发布了,文章较长未做分割。同时也省略了很大一部分内容,重点主要在线程等待通知机制ReentrantLock的使用这两大部分(BTW : 想要电子书的可以在文章下方留言)。

1.Java多线程基础

1.1 进程与多线程概念:

​ 进程:进程是操作系统管理的基础运行单元

​ 线程:线程为进程中独立运行的子任务

多线程通过异步执行任务的方式,提高了系统的运行效率

1.2 使用多线程

1.2.1 继承Thread类

public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }

    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("myThread is running.....");
        }
    }

1.2.2 实现Runnable接口

public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread myThread = new Thread(myRunnable, "myThread");//具体参照Thread的构造函数
        myThread.start();
    }


    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("Thread created by runnable interface");
        }
    }

上述两种创建线程的方式可灵活选择,如果要创建的线程类要继承其它类的话,则采取实现Runnable接口的实现方式,因为Java不支持多继承,却支持多实现。

1.2.3使用Callable和Future或FutureTask创建线程

1.2.3.1使用Callable和Future创建
public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        MyTask myTask = new MyTask();
        Future<Integer> submit = pool.submit(myTask);
        try {
            System.out.println(submit.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }


    static class MyTask implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            return 5;
        }
    }
1.2.3.2使用Callable和FutureTask创建
public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 5;
            }
        });
        pool.submit(task);
        try {
            System.out.println(task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

这种实现方式要配合线程池使用。

1.2.4三种创建方式对比

  1. 使用继承Thread就不可以继承其他类
  2. 实现Callable接口的方式相比与实现Runable接口,可以获取返回值同时可以抛出处理异常。(一般推荐采用实现接口的方式来创建多线程)

1.2.5 start()与run()方法区别

start()方法是线程的启动方法,会开启新线程;run()方法不会开启新线程,跟普通方法调用没什么区别。

1.3.实例变量与线程安全

1.3.1 不共享数据

public static void main(String[] args) {
        MyThread a = new MyThread("A");
        MyThread b = new MyThread("B");
        MyThread c = new MyThread("C");
        MyThread d = new MyThread("D");
        MyThread e = new MyThread("E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }


    static class MyThread extends Thread{
        private int count = 5;

        public MyThread(String name){
            super(name);
        }

        @Override
        public void run() {
            --count;
            System.out.println(Thread.currentThread().getName()+"执行: count = "+count);
        }
    }

/*****************************************执行结果************************************************/
/**
A执行: count = 4
B执行: count = 4
D执行: count = 4
C执行: count = 4
E执行: count = 4*/

1.3.2共享数据

public static void main(String[] args) {
        MyThread thread = new MyThread();
        Thread a = new Thread(thread,"A");
        Thread b = new Thread(thread,"B");
        Thread c = new Thread(thread,"C");
        Thread d = new Thread(thread,"D");
        Thread e = new Thread(thread,"E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }


    static class MyThread extends Thread{
        private int count = 5;

        public MyThread(){}

        public MyThread(String name){
            super(name);
        }

        @Override
        public void run() {
            count--;
            System.out.println(Thread.currentThread().getName()+"执行: count = "+count);
        }
    }
/*****************************************运行结果**********************************************/
/**
A执行: count = 3
B执行: count = 3
D执行: count = 2
C执行: count = 1
E执行: count = 0
*/

从结果中可以看出A线程B线程的结果都是3,此处产生了线程安全的问题,具体产生的原因可以查阅JVM中 --操作的原理,同时通过synchronized来实现线程安全,关于这方面的问题可以查看本人JVM相关模块的文章。

1.4.停止线程

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。(推荐)
  2. 使用stop方法强制终止线程,和suspend以及resume方法一样,都是过期作废的方法,使用它们可能回产生不可预料的结果。(不推荐)
  3. 使用interrupt方法中断线程。

1.5线程常用API(略)

2 对象及变量的并发访问

2.1 Synchronized 同步方法

方法中的变量不存在线程安全的相关问题,线程安全问题只存在于被多个线程共享的实列变量中。使用Synchronized的修饰的方法为同步方法,能够解决部分线程安全的问题。

2.2 静态同步synchronized 方法与 synchronized (class) 代码块

  1. 如果synchronized 修饰的是static静态方法,那么就是对当前文件所对应的Class类进行加锁。
  2. sychronized修饰非静态方法是给对象加锁。
  3. synchronized(class)代码块的作用和 synchronized static 方法的作用一样,都是给Class类进行加锁。

2.3 Synchronized 与String 类型常量池使用注意事项

将synchronized(String)同步块与String联合使用时,要注意一下常量池带来的一些例外。

public static void main(String[] args) {
        MyThread thread = new MyThread("AA");
//        MyThread thread = new MyThread(new Object());
        Thread a = new Thread(thread,"A");
        MyThread thread1 = new MyThread("AA");
//        MyThread thread1 = new MyThread(new Object());
        Thread b = new Thread(thread1,"B");

        a.start();
        b.start();

    }


    static class MyThread extends Thread{

        private Object objLock;

        public MyThread(Object objLock) {
            this.objLock = objLock;
        }

        @Override
        public void run() {
            try {
                synchronized (objLock){
                    while (true){
                        System.out.println(Thread.currentThread().getName()+"    is running!!!!! ");
                        Thread.sleep(1000);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
/*******************************************输出结果**********************************************/
/**
A    is running!!!!! 
A    is running!!!!! 
A    is running!!!!! 
A    is running!!!!! 
A    is running!!!!! 
A    is running!!!!! 
*/

当使用字符串"AA"做锁对象时,会发现同步块会被线程独占,因此可以得知线程a和线程b持有的是同一把锁。

2.4 synchronized 方法无限等待与解决

对不要求强一致性的方法采用不同的锁对象或者采用同步块与同步方法配合使用的方式来避免不必要的线程无线等待。

2.4.1 多线程的死锁

不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成,造成线程的“假死”状态。

public static void main(String[] args) {
        DeadThread deadThread = new DeadThread();
        Thread deadOne = new Thread(deadThread, "a");
        Thread deadTwo = new Thread(deadThread, "b");
        deadOne.start();
        deadTwo.start();
    }


    static class DeadThread extends Thread{

        Object obj1 = new Object();
        Object obj2 = new Object();

        @Override
        public void run() {
            if("a".equals(currentThread().getName())){
                synchronized (obj1){
                    System.out.println(currentThread().getName()+"  locked obj1  waiting for obj2");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj2){
                        System.out.println(currentThread().getName() + " locked obj2");
                    }
                }
            }
            if ("b".equals(currentThread().getName())){
                synchronized (obj2){
                    System.out.println(currentThread().getName()+"  locked obj2  waiting for obj1");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj1){
                        System.out.println(currentThread().getName() + " locked obj1");
                    }
                }
            }
        }
    }

可以使用JDK自带的监测工具来检测是否有死锁现象发生,进入到JDK安装的bin目录,先执行Jps命令查找出运行线程的pid,然后通过jstack -l pid就可以查看线程死锁相关信息了

2. 5 volatile 关键字

volatile关键字的主要作用是保证变量操作的可见性、有序性,但不保证其操作的原子性。因此volatile关键字并不能真正的实现线程安全,只是线程同步的轻量级实现。关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的是多个线程之间访问资源的同步性。

3. 线程间通信

3.1 等待/通知机制(wait/notify)

  1. wait() 方法可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。

  2. notify() 方法可以随机唤醒等待队列中等待同一共享资源的"一个"线程,并使该线程退出等待队列,进入可运行状态,也就是notify()方法仅通知"一个"线程。

  3. notifyAll() 方法可以使所有正在等待队列中等待同一共享资源的"全部"线程从等待状态退出,进入可运行状态。

    注意,wait()方法被执行后,锁会被自动释放,但执行完notify()方法,锁不自动释放;当线程呈wait()状态时,调用线程对象的interrupt()方法会出现 InterruptedException异常。

3.2生产者/消费者模式的实现

3.3通过管道进行线程间通信

3.4 ThreadLocal的使用

4. Lock的使用

4.1 ReentrantLock

4.1.1 使用 ReentrantLock 实现同步

public static void main(String[] args) {
        MyTask task = new MyTask();
        Thread t1 = new MyThread(task);
        Thread t2 = new MyThread(task);
        Thread t3 = new MyThread(task);
        Thread t4 = new MyThread(task);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

    static class MyTask{
        private Lock lock = new ReentrantLock();

        public void method(){
            //lock.lock();
            for (int i = 0 ; i < 5; i++){
                System.out.println("Thread name = "+Thread.currentThread().getName()+"   "+i);
            }
            //lock.unlock();
        }
    }

    static class MyThread extends Thread{

        private MyTask task;

        public MyThread(MyTask task) {
            this.task = task;
        }

        @Override
        public void run() {
            task.method();
        }
    }
/**************************************输出结果***************************************************/
/**
Thread name = Thread-0   0
Thread name = Thread-1   0
Thread name = Thread-0   1
Thread name = Thread-1   1
Thread name = Thread-2   0
Thread name = Thread-0   2
Thread name = Thread-2   1
Thread name = Thread-1   2
Thread name = Thread-2   2
Thread name = Thread-0   3
Thread name = Thread-2   3
Thread name = Thread-1   3
Thread name = Thread-2   4
Thread name = Thread-0   4
Thread name = Thread-1   4
Thread name = Thread-3   0
Thread name = Thread-3   1
Thread name = Thread-3   2
Thread name = Thread-3   3
Thread name = Thread-3   4
此时,线程之间不是同步执行的,将注释处的代码取消掉,就可以看见线程同步执行的效果了
*/

4.1.2 使用Condition实现等待/通知

ReentrantLock借助Condition对象可以实现等待/通知模型,在一个Lock对象里面可以创建多个Condition实例,线程对象可以注册在指定的Condition中,从而可以有选择地进行线程通知,在调度线程上更加灵活。

4.1.2.1 使用Condition实现线程一对一等待/通知
public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        WaitThread waitThread = new WaitThread(lock, condition);
        waitThread.setName("waitThread");
        NotifyThread notifyThread = new NotifyThread(lock, condition);
        notifyThread.setName("notifyThread");
        waitThread.start();
        notifyThread.start();
    }

    static class WaitThread extends Thread{

        private Lock lock;

        private Condition condition;

        public WaitThread(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }


        @Override
        public void run() {
            try {
                lock.lock();
                System.out.println("Thread : "+currentThread().getName()+" is waiting");
                condition.await();//注意是await()方法
                System.out.println("Thread "+currentThread().getName()+" : I'm wake up cause someone notify me ");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }


    static class NotifyThread extends Thread{

        private Lock lock;

        private Condition condition;

        public NotifyThread(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }


        @Override
        public void run() {
            try {
                lock.lock();
                Thread.currentThread().sleep(2000);
                System.out.println("Thread : "+currentThread().getName()+" call waiting thread");
                condition.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
/*********************************************执行结果*******************************************/
/**
Thread : waitThread is waiting
Thread : notifyThread call waiting thread
Thread waitThread : I'm wake up cause someone notified me
*/
4.1.2.2 使用多个Condition通知部分线程
public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition conditionA = lock.newCondition();
        Condition conditionB = lock.newCondition();
        WaitThread baseWaitA= new WaitThread(lock, conditionA);
        WaitThread baseWaitB= new WaitThread(lock, conditionB);
        Thread waitThread1 = new Thread(baseWaitA,"wait on Condition A T1");
        Thread waitThread2 = new Thread(baseWaitA,"wait on Condition A T2");
        Thread waitThread3 = new Thread(baseWaitB,"wait on Condition B T3");
        Thread waitThread4 = new Thread(baseWaitB,"wait on Condition B T4");

        NotifyThread baseNotify = new NotifyThread(lock, conditionB);
        Thread notifyBThread = new Thread(baseNotify,"i'll notify all Thread which waiting on Condition B");

        waitThread1.start();
        waitThread2.start();
        waitThread3.start();
        waitThread4.start();
        notifyBThread.start();
    }

    static class WaitThread extends Thread{

        private Lock lock;

        private Condition condition;

        public WaitThread(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }


        @Override
        public void run() {
            try {
                lock.lock();
                System.out.println("Thread : "+currentThread().getName()+" is waiting");
                condition.await();//注意是await()方法
                System.out.println("Thread "+currentThread().getName()+" : I'm wake up cause someone notified me ");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }


    static class NotifyThread extends Thread{

        private Lock lock;

        private Condition condition;

        public NotifyThread(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }


        @Override
        public void run() {
            try {
                lock.lock();
                Thread.currentThread().sleep(2000);
                System.out.println(currentThread().getName());
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
/*********************************************运行结果********************************************/
/**
Thread : wait on Condition A T1 is waiting
Thread : wait on Condition A T2 is waiting
Thread : wait on Condition B T3 is waiting
Thread : wait on Condition B T4 is waiting
i'll notify all Thread which waiting on Condition B
Thread wait on Condition B T3 : I'm wake up cause someone notified me 
Thread wait on Condition B T4 : I'm wake up cause someone notified me
成功唤醒所有在Condition B上等待的线程
*/

4.1.3 ReentrantLock 实现生产者/消费者模式

写法1
public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition producerCondition = lock.newCondition();
        Condition consumerCondition = lock.newCondition();

        Producer producer = new Producer(lock, consumerCondition, producerCondition);
        Thread p1 = new Thread(producer, "P1");
        Thread p2 = new Thread(producer, "P2");
        Consumer consumer = new Consumer(lock, consumerCondition, producerCondition);
        Thread c1 = new Thread(consumer, "C1");
        Thread c2 = new Thread(consumer, "C2");
        Thread c3 = new Thread(consumer, "C3");

        p1.start();
        p2.start();
        c1.start();
        c2.start();
        c3.start();
    }


    static class Producer extends Thread{
        private Lock lock;

        private Condition consumerCondition;

        private Condition producerCondition;

        public Producer(Lock lock, Condition consumerCondition, Condition producerCondition) {
            this.lock = lock;
            this.consumerCondition = consumerCondition;
            this.producerCondition = producerCondition;
        }

        @Override
        public void run() {
            while (true){
                try {
                    lock.lock();
                    Thread.sleep(500);
                    if (list.size() > 10){
                        consumerCondition.signalAll();
                        System.out.println(currentThread().getName()+" notified consumer and waiting");
                        producerCondition.await();
                    }else {
                        list.add(0);
                        System.out.println(currentThread().getName()+" 生产了一个产品  list size : "+list.size());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }


    static class Consumer extends Thread{

        private Lock lock;

        private Condition consumerCondition;

        private Condition producerCondition;

        public Consumer(Lock lock, Condition consumerCondition, Condition producerCondition) {
            this.lock = lock;
            this.consumerCondition = consumerCondition;
            this.producerCondition = producerCondition;
        }

        @Override
        public void run() {
            while (true){
                try {
                    lock.lock();
                    if (list.size()<3){
                        System.out.println(currentThread().getName()+" is waiting list size : "+list.size());
                        producerCondition.signalAll();
                        consumerCondition.await();
                    }else {
                        Thread.sleep(1500);
                        list.remove(0);
                        System.out.println(currentThread().getName()+"  consumer one list size : "+list.size());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
写法2
public static void main(String[] args) {
        Service service = new Service();
        ProducerThread producerThread = new ProducerThread(service);
        Thread p1 = new Thread(producerThread, "P1");
        Thread p2 = new Thread(producerThread, "P2");

        ConsumerThread consumerThread = new ConsumerThread(service);
        Thread c1 = new Thread(consumerThread, "c1");
        Thread c2 = new Thread(consumerThread, "c2");
        Thread c3 = new Thread(consumerThread, "c3");
        Thread c4 = new Thread(consumerThread, "c4");

        p1.start();
        p2.start();
        c1.start();
        c2.start();
        c3.start();
        c4.start();
    }


    static class ProducerThread extends Thread{
        private Service service;

        public ProducerThread(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.produce();
        }
    }

    static class ConsumerThread extends Thread{

        private Service service;

        public ConsumerThread(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.consumer();
        }
    }

    static class Service{
        private List<String> list = new ArrayList<>();
        Lock lock = new ReentrantLock();
        private Condition producerCondition = lock.newCondition();
        private Condition consumerCondition = lock.newCondition();

        public void produce(){
            while (true){
                try {
                    lock.lock();
                    Thread.sleep(500);
                    if (list.size() > 10){
                        consumerCondition.signalAll();
                        System.out.println(Thread.currentThread().getName()+" notified consumer and waiting");
                        producerCondition.await();
                    }
                    list.add("");
                    System.out.println(Thread.currentThread().getName()+" 生产了一个产品  list size : "+list.size());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }

        public void consumer(){
            while (true){
                try {
                    lock.lock();
                    if (list.size()<3){
                        System.out.println(Thread.currentThread().getName()+" is waiting list size : "+list.size());
                        producerCondition.signalAll();
                        consumerCondition.await();
                    }else {
                        Thread.sleep(1500);
                        list.remove(0);
                        System.out.println(Thread.currentThread().getName()+"  consumer one list size : "+list.size());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }
    }

4.1.4 公平锁与非公平锁

4.1.5 ReentrantLock类相关方法

4.1.5.1 方法getHoldCount() , getQueueLenght() , 和 getWaitQueueLength()
  1. getHoldCount() :查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。

    public static void main(String[] args) {
            Myservice myservice = new Myservice();
            GetHoldCountThread getHoldCountThread = new GetHoldCountThread(myservice);
            getHoldCountThread.setName("getHoldCountThread");
            getHoldCountThread.start();
        }
    
        static class Myservice{
            private Lock lock = new ReentrantLock();
    
            public void invoke(){
                try {
                    lock.lock();
                    System.out.println("第1次输出 Thread "+Thread.currentThread().getName()+" getHoldCount : "+((ReentrantLock) lock).getHoldCount());
                    reInvoke();
                    Thread.sleep(2000);
                    System.out.println("第3次输出 Thread "+Thread.currentThread().getName()+" getHoldCount : "+((ReentrantLock) lock).getHoldCount());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                    System.out.println("最后次输出 Thread "+Thread.currentThread().getName()+" getHoldCount : "+((ReentrantLock) lock).getHoldCount());
                }
    
            }
    
            public void reInvoke(){
                try {
                    lock.lock();
                    Thread.sleep(2000);
                    System.out.println("第2次输出 Thread "+Thread.currentThread().getName()+" getHoldCount : "+((ReentrantLock) lock).getHoldCount());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    
        static class GetHoldCountThread extends Thread{
    
            private Myservice myservice;
    
            public GetHoldCountThread(Myservice myservice) {
                this.myservice = myservice;
            }
    
            @Override
            public void run() {
                myservice.invoke();
            }
        }
    /*****************************************输出结果********************************************/
    /**
    第1次输出 Thread getHoldCountThread getHoldCount : 1
    第2次输出 Thread getHoldCountThread getHoldCount : 2
    第3次输出 Thread getHoldCountThread getHoldCount : 1
    最后次输出 Thread getHoldCountThread getHoldCount : 0
    */
    
  2. getQueueLength()的作用是返回等待获取此锁的线程估计数,比如有5个线程,1个线程首先执行await()方法,那么在调用getQueueLength()方法后的返回值就是4,即还有四个线程在等待lock的释放。

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
            List<Thread> threads = new ArrayList<>();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" 进入方法 ");
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            for (int i = 0 ;i < 10 ; i++){
                Thread thread = new Thread(runnable);
                threads.add(thread);
            }
    
            for (int i = 0 ; i<threads.size(); i++){
                threads.get(i).start();
            }
    
            Thread.sleep(2000);
            System.out.println("还剩 "+((ReentrantLock) lock).getQueueLength()+" 线程在等待获取锁!");
        }
    /*******************************************输出结果***************************************/
    /**
    Thread-0 进入方法 
    还剩 9 线程在等待获取锁!
    */
    
  3. getWaitQueueLength(Condition condition)的作用是返回在指定条件condition上等待的线程数。比如有5个线程都在同一个condition上await(),那么调用此方法时返回的值则为5。

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
            Condition waitOnOdd = lock.newCondition();
            List<Thread> threads = new ArrayList<>();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
    
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" 进入方法 ");
                        if ("1,3,5,9".contains(Thread.currentThread().getName())){
                            waitOnOdd.await();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            for (int i = 0 ;i < 10 ; i++){
                Thread thread = new Thread(runnable);
                thread.setName(""+i);
                threads.add(thread);
            }
    
            for (Thread t : threads){
                t.start();
            }
    
            Thread.sleep(2000);
            lock.lock();//注意,不在lock块中调用getWaitQueueLength方法会抛出IllegalMonitorStateException
            System.out.println(((ReentrantLock) lock).getWaitQueueLength(waitOnOdd)+" 个线程 waiting on oddCondition");
            lock.unlock();
        }
    /*******************************************输出********************************************/
    /**
    0 进入方法 
    1 进入方法 
    2 进入方法 
    3 进入方法 
    4 进入方法 
    5 进入方法 
    6 进入方法 
    7 进入方法 
    8 进入方法 
    9 进入方法 
    4 个线程 waiting on oddCondition
    */
    
4.1.5.2 hasQueuedThread(),hasQueueThreds(),hasWaiters()方法
  1. hasQueuedThread(Thread thread)查询指定的线程是否正在等待获取锁。

  2. hasQueueThreads()查询是否有线程正在等待获取此锁。

    public static void main(String[] args) throws Exception{
            ReentrantLock lock = new ReentrantLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" 进入方法 ");
                        Thread.currentThread().sleep(Integer.MAX_VALUE);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Thread t1 = new Thread(runnable);
            t1.setName("T1");
            t1.start();
    
            Thread.sleep(50);
    
            Thread t2 = new Thread(runnable);
            t2.start();
            Thread.sleep(500);
            System.out.println(lock.hasQueuedThread(t2));
            System.out.println(lock.hasQueuedThreads());
        }
    /******************************************输出结果*******************************************/
    /**
    T1 进入方法 
    true
    true
    */
    
  3. hasWaiters(Condition condition)的作用时查询是否有线程正在等待与此锁有关的condition条件。

    public static void main(String[] args) throws Exception{
            ReentrantLock lock = new ReentrantLock();
            Condition waitCondition = lock.newCondition();
            List<Thread> waits = new ArrayList<>();
            Runnable waitRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        waitCondition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Runnable notifyRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println("有没有线程在waitCondition上等待 ? "+lock.hasWaiters(waitCondition)+" 等待线程数: "+lock.getWaitQueueLength(waitCondition));
                        waitCondition.signal();
                        System.out.println("有没有线程在waitCondition上等待 ? "+lock.hasWaiters(waitCondition)+" 等待线程数: "+lock.getWaitQueueLength(waitCondition));
                        waitCondition.signalAll();
                        System.out.println("有没有线程在waitCondition上等待 ? "+lock.hasWaiters(waitCondition)+" 等待线程数: "+lock.getWaitQueueLength(waitCondition));
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            for (int i = 0 ; i < 10 ; i++){
                Thread thread = new Thread(waitRunnable);
                waits.add(thread);
            }
    
            for (Thread t : waits){
                t.start();
            }
    
            Thread.sleep(500);
            Thread notify = new Thread(notifyRunnable);
            notify.start();
        }
    /**********************************************输出结果**************************************/
    /**
    有没有线程在waitCondition上等待 ? true 等待线程数: 10
    有没有线程在waitCondition上等待 ? true 等待线程数: 9
    有没有线程在waitCondition上等待 ? false 等待线程数: 0
    */
    
4.1.5.3 isFair(),isHeldByCurrentThread(),isLocked()方法
  1. isFair() 判断锁是不是公平锁

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock(true);
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    System.out.println("是否是公平锁 : "+((ReentrantLock) lock).isFair());
                    lock.unlock();
                }
            }).start();
        }
    
  2. isHeldByCurrentThread() 的作用是查询当前线程是否保持此锁

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("是否被当前线程持有锁 : "+((ReentrantLock) lock).isHeldByCurrentThread());
                    lock.lock();
                    System.out.println("是否被当前线程持有锁 : "+((ReentrantLock) lock).isHeldByCurrentThread());
                    lock.unlock();
                }
            }).start();
        }
    /**********************************************输出结果**************************************/
    /**
    是否被当前线程持有锁 : false
    是否被当前线程持有锁 : true
    */
    
  3. isLocked() 查询此锁是否由任意线程持有

    public static void main(String[] args) throws Exception{
        Lock lock = new ReentrantLock();
    
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("是否被线程持有锁 : "+((ReentrantLock) lock).isLocked());
                lock.lock();
                System.out.println("是否被线程持有锁 : "+((ReentrantLock) lock).isLocked());
                lock.unlock();
            }
        }).start();
     }
    /**********************************************输出结果**************************************/
    /**
    是否被当前线程持有锁 : false
    是否被当前线程持有锁 : true
    */
    
4.1.5.4 lockInterruptibly(),tryLock()和tryLock(long timeout,TimeUnit unit) 方法
  1. lockInterruptibly(),如果当前线程未被中断,则获取锁定,如果已经中断则抛出异常

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
    
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
    //                    lock.lock();
                        lock.lockInterruptibly();
                        System.out.println(Thread.currentThread().getName()+" is running ..... ");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            });
            t.setName("T1");
            t.start();
            t.interrupt();
        }
    /**********************************************输出结果**************************************/
    /**
    java.lang.InterruptedException
     at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
     at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
     at leonardo.ezio.springboot.demo2.Test.GetHoldCount$1.run(GetHoldCount.java:21)
     at java.lang.Thread.run(Thread.java:745)
    */
    
  2. tryLock(),仅在调用时锁未被另一线程占有的情况下,才获取该锁

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    boolean flag = lock.tryLock();
                    System.out.println(Thread.currentThread().getName()+" tryLock "+lock.tryLock());
                    if (flag){
                        System.out.println(Thread.currentThread().getName()+" 获得锁 ");
                    }else {
                        System.out.println(Thread.currentThread().getName()+" 没有获得锁 ");
                    }
                }
            };
    
            Thread t1 = new Thread(runnable);
            Thread t2 = new Thread(runnable);
    
            t1.setName("T1");
            t2.setName("T2");
    
            t1.start();
            t2.start();
        }
    /*********************************************输出结果*************************************/
    /**
    T2 tryLock false
    T2 没有获得锁 
    T1 tryLock true
    T1 获得锁 
    */ 
    
  3. tryLock(long timeout,TimeUnit unit) ,如果锁在给定等待时间内,没有被另一个线程保持,且当前线程未被中断,则获取该锁

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+" tryLock  currentTime : "+System.currentTimeMillis());
                        if (lock.tryLock(3,TimeUnit.SECONDS)){
                            System.out.println(Thread.currentThread().getName()+" 获得锁 currentTime : "+System.currentTimeMillis());
                        }else {
                            System.out.println(Thread.currentThread().getName()+" 没有获得锁 currentTime : "+System.currentTimeMillis());
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (((ReentrantLock) lock).isHeldByCurrentThread()){
                            System.out.println(Thread.currentThread().getName()+" is hold lock ");
                            if(Thread.currentThread().getName().equals("T1")){
                                try {
                                    Thread.sleep(3100);//T1睡眠若超过 给定的等待时间,则T2获取不到锁
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                            lock.unlock();
                        }
                    }
                }
            };
    
            Thread t1 = new Thread(runnable);
            Thread t2 = new Thread(runnable);
    
            t1.setName("T1");
            t2.setName("T2");
    
            t1.start();
            Thread.sleep(50);
            t2.start();
        }
    /*********************************************输出结果*************************************/
    /**
    T1 tryLock  currentTime : 1556248231940
    T1 获得锁 currentTime : 1556248231941
    T1 is hold lock 
    T2 tryLock  currentTime : 1556248231990
    T2 没有获得锁 currentTime : 1556248234991 
    */ 
    
4.1.5.5 awaitUninterruptibly(),awaitUntil() 方法
  1. awaitUninterruptibly(),线程await的时候被interrupt会抛出异常,若是awaitUninterruptibly则不会

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" begin waiting ");
                        condition.awaitUninterruptibly();
    //                    condition.await();
                        System.out.println(Thread.currentThread().getName()+" end waiting ");
    //                } catch (InterruptedException e) {
    //                    e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Thread thread = new Thread(runnable, "T1");
            thread.start();
            thread.interrupt();
        }
    
  2. awaitUntil(),线程有限期等待,类似Object的wait(long millis)

     public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        Calendar calendar = Calendar.getInstance();
                        calendar.add(Calendar.SECOND,10);
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" begin waiting current time : "+System.currentTimeMillis());
                        condition.awaitUntil(calendar.getTime());
                        System.out.println(Thread.currentThread().getName()+" end waiting current time : "+System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Thread thread = new Thread(runnable, "T1");
            thread.start();
        }
    /*********************************************输出结果*************************************/
    /**
    T1 begin waiting current time : 1556249594276
    T1 end waiting current time : 1556249604269
    */ 
    

    注:线程等待期间,可以被其他线程唤醒。

4.1.6 ReentrantReadWriteLock

ReentrantReadWriteLock类中包含两个锁:一个是读相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。

  1. 读锁共享

    public static void main(String[] args) throws Exception{
            ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        rwLock.readLock().lock();
                        System.out.println(Thread.currentThread().getName()+" get readLock "+System.currentTimeMillis());
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        rwLock.readLock().unlock();
                    }
                }
            };
    
            Thread t1 = new Thread(runnable, "T1");
            Thread t2 = new Thread(runnable, "T2");
            t1.start();
            t2.start();
        }
    /*****************************************************************************************/
    /**
    T1 get readLock 1556259700048
    T2 get readLock 1556259700048
    */
    

    T1 和T2是同时获得锁的,因此读锁与读锁之间可以共享。

  1. 写锁互斥

    public static void main(String[] args) throws Exception{
            ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        rwLock.writeLock().lock();
                        System.out.println(Thread.currentThread().getName()+" get readLock "+System.currentTimeMillis());
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        rwLock.writeLock().unlock();
                    }
                }
            };
    
            Thread t1 = new Thread(runnable, "T1");
            Thread t2 = new Thread(runnable, "T2");
            t1.start();
            t2.start();
        }
    /*************************************************************************************/
    /**
    T1 get readLock 1556260282635
    T2 get readLock 1556260283636
    */
    

    T1和T2存在竞争锁的情况,总有一个线程要等另一个线程释放锁之后才能尝试获取锁

  1. 读写锁互斥

    public static void main(String[] args) throws Exception{
            ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    
            Runnable writeRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        rwLock.writeLock().lock();
                        System.out.println(Thread.currentThread().getName()+" get writeLock "+System.currentTimeMillis());
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        rwLock.writeLock().unlock();
                    }
                }
            };
    
            Runnable readRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        rwLock.readLock().lock();
                        System.out.println(Thread.currentThread().getName()+" get readLock "+System.currentTimeMillis());
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        rwLock.readLock().unlock();
                    }
                }
            };
    
            Thread t1 = new Thread(writeRunnable, "T1");
            Thread t2 = new Thread(readRunnable, "T2");
            t2.start();
            Thread.sleep(50);
            t1.start();
        }
    /********************************************************************************************/
    /**
    T2 get readLock 1556260986179
    */
    

    T1一直在等待T2释放读锁

经过以上讲解,我们可以完全使用Lock对象来代替synchronized关键字,同时Lock具有一些synchronized所不具备的功能,掌握Lock有助于理解并发包中源代码的实现原理。

5 定时器Timer

定时器这里不做过多讲述,就提一下值得注意的两点。

  1. 当任务执行时间超过任务的循环间隔时,下一次任务开始的时间与正常情况下是不同的。
  2. 任务的追赶性,如果我们设置任务开始的时间在当前时间之前,那么他会计算出两个时间的差,计算出时间差内能够执行任务多少次,然后立即执行。scheduleAtFixedRate具有追赶性,而Schedule不具有追赶性。

6 单例模式与多线程(略)

7 线程组(略)

上一篇下一篇

猜你喜欢

热点阅读