多线程通信的两种方式? (可重入锁ReentrantLock和Object)
(一)java中线程协作的最常见的两种方式:
(1)利用object的wait()、notify()和notifyall()方法及synchronized
(2)使用condition、reentrantlock
(二)object类的wait()、notify()和notifyall()方法
1 /** 2 * wakes up a single thread that is waiting on this object's 3 * monitor. if any threads are waiting on this object, one of them 4 * is chosen to be awakened. the choice is arbitrary and occurs at 5 * the discretion of the implementation. a thread waits on an object's 6 * monitor by calling one of the wait methods 7 */ 8 public final native void notify(); 9 10 /** 11 * wakes up all threads that are waiting on this object's monitor. a 12 * thread waits on an object's monitor by calling one of the 13 * wait methods. 14 */ 15 public final native void notifyall(); 16 17 /** 18 * causes the current thread to wait until either another thread invokes the 19 * {@link java.lang.object#notify()} method or the 20 * {@link java.lang.object#notifyall()} method for this object, or a 21 * specified amount of time has elapsed. 22 * <p> 23 * the current thread must own this object's monitor. 24 */ 25 public final native void wait(long timeout) throws interruptedexception;
从这三个方法的文字描述可以知道以下几点信息:
1)wait()、notify()和notifyall()方法是本地方法,并且为final方法,无法被重写。
2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
4)调用notifyall()方法能够唤醒所有正在等待这个对象的monitor的线程;
(三)lock接口,reentrantlock、condition说明
3.1重要概念:可重入性
可重入性描述这样的一个问题:一个线程在持有一个锁的时候,它内部能否再次(多次)申请该锁。如果一个线程已经获得了锁,其内部还可以多次申请该锁成功。那么我们就称该锁为可重入锁。通过以下伪代码说明:
1 void methoda(){ 2 lock.lock(); // 获取锁 3 methodb(); 4 lock.unlock() // 释放锁 5 } 6 7 void methodb(){ 8 lock.lock(); // 获取锁 9 // 其他业务 10 lock.unlock();// 释放锁 11 }
可重入锁可以理解为锁的一个标识。该标识具备计数器功能。标识的初始值为0,表示当前锁没有被任何线程持有。每次线程获得一个可重入锁的时候,该锁的计数器就被加1。每次一个线程释放该所的时候,该锁的计数器就减1。前提是:当前线程已经获得了该锁,是在线程的内部出现再次获取锁的场景
3.2 reentrantlock实现说明
该demo模拟电影院的售票情况,tickets总票数。开启了10个窗口售票,售完为止
1 public class reentrantlockdemo01 implements runnable { 2 3 private lock lock = new reentrantlock(); 4 5 private int tickets = 200; 6 7 @override 8 public void run() { 9 while (true) { 10 lock.lock(); // 获取锁 11 try { 12 if (tickets > 0) { 13 timeunit.milliseconds.sleep(100); 14 system.out.println(thread.currentthread().getname() + " " + tickets--); 15 } else { 16 break; 17 } 18 } catch (interruptedexception e) { 19 e.printstacktrace(); 20 } finally { 21 lock.unlock(); // 释放所 22 } 23 } 24 } 25 26 public static void main(string[] args) { 27 reentrantlockdemo01 reentrantlockdemo = new reentrantlockdemo01(); 28 for (int i = 0; i < 10; i++) { 29 thread thread = new thread(reentrantlockdemo, "thread" + i); 30 thread.start(); 31 } 32 } 33 }
3.3lockinterruptibly()方法说明
从lock的源码可以看出:lockinterruptibly() 抛出中断异常
1 void lockinterruptibly() throws interruptedexception;
3.4 trylock(),trylock(long time, timeunit unit)方法说明
trylock()方法立刻返回当前获取情况。
trylock(long time, timeunit unit)等待一定的时间,返回获取情况
1 public class reentrantlockdemo03 implements runnable { 2 3 private reentrantlock lock = new reentrantlock(); 4 5 @override 6 public void run() { 7 try { 8 if (lock.trylock(2, timeunit.seconds)) { 9 system.out.println(thread.currentthread().getname() + " 获取当前lock锁"); 10 timeunit.seconds.sleep(4); 11 } else { 12 system.out.println(thread.currentthread().getname()+ " 获取锁失败"); 13 } 14 } catch (interruptedexception e) { 15 e.printstacktrace(); 16 } finally { 17 if (lock.isheldbycurrentthread()) { 18 lock.unlock(); 19 } 20 } 21 } 22 23 24 public static void main(string[] args) { 25 reentrantlockdemo03 reentrantlockdemo = new reentrantlockdemo03(); 26 thread thread01 = new thread(reentrantlockdemo, "thread01"); 27 thread thread02 = new thread(reentrantlockdemo, "thread02"); 28 thread01.start(); 29 thread02.start(); 30 }
3.5 newcondition() 方法说明
目前只是对newcondition()使用方式进行说明,没有深入的分析condition()的实现源码。
condition的作用是对锁进行更精确的控制。condition中的await()方法相当于object的wait()方法,condition中的signal()方法相当于object的notify()方法,condition中的signalall()相当于object的notifyall()方法。不同的是,object中的wait(),notify(),notifyall()方法是和”同步锁”(synchronized关键字)捆绑使用的;而condition是需要与”互斥锁”/”共享锁”捆绑使用的。
3.6 condition
condition是在java 1.5中才出现的,它用来替代传统的object的wait()、notify()实现线程间的协作,相比使用object的wait()、notify(),使用condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用condition,在阻塞队列那一篇博文中就讲述到了,阻塞队列实际上是使用了condition来模拟线程间协作。
- condition是个接口,基本的方法就是await()和signal()方法;
- condition依赖于lock接口,生成一个condition的基本代码是lock.newcondition()
- 调用condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
conditon中的await()对应object的wait();
condition中的signal()对应object的notify();
condition中的signalall()对应object的notifyall()。
(四)两种方式的多线程通信的实现
1 package cn.csrc.base.cpu; 2 import java.util.concurrent.locks.condition; 3 import java.util.concurrent.locks.reentrantlock; 4 /** 5 * 6 *功能说明:线程间通信的两种方式 (1)object (2)reentrantlock 7 *@author:zsq 8 *create date:2019年7月2日 下午4:23:41 9 *修改人 修改时间 修改描述 10 *copyright 11 */ 12 public class oddevenprinter { 13 14 //第一种方法 object作为锁 15 private final object obj=new object(); 16 17 //第二种方法 18 private final reentrantlock lock=new reentrantlock(); 19 private final condition condition=lock.newcondition(); 20 21 private int limit; 22 private volatile int count; 23 24 public oddevenprinter(int limit,int count){ 25 this.limit=limit; 26 this.count=count; 27 } 28 29 //object锁 30 public void myprint1(){ 31 synchronized (obj) { 32 while(count<limit){ 33 try { 34 system.out.println(string.format("线程[%s]打印数字:%d",thread.currentthread().getname(),++count)); 35 obj.notifyall(); 36 obj.wait(); 37 } catch (exception e) { 38 e.printstacktrace(); 39 } 40 } 41 } 42 } 43 44 //reentrantlock 重入锁 45 public void myprint2(){ 46 //一进入就加锁 47 lock.lock(); 48 try{ 49 while(count<limit){ 50 system.out.println(string.format("线程[%s]打印数字:%d",thread.currentthread().getname(),++count)); 51 condition.signalall();//唤醒被锁住的线程 52 } 53 } catch (exception e) { 54 e.printstacktrace(); 55 }finally{ 56 //最后释放锁 57 lock.unlock(); 58 } 59 } 60 61 public static void main(string[] args) throws interruptedexception { 62 oddevenprinter print = new oddevenprinter(10, 0); 63 system.err.println("-----------第一种方法 object-----------"); 64 thread thread1 = new thread(print::myprint1, "thread-a"); 65 thread thread2 = new thread(print::myprint1, "thread-b"); 66 thread1.start(); 67 thread2.start(); 68 thread.sleep(1000); 69 70 system.err.println("-----------第二种方法 lock-----------"); 71 thread thread3 = new thread(print::myprint2, "thread-c"); 72 thread thread4 = new thread(print::myprint2, "thread-d"); 73 thread3.start(); 74 thread4.start(); 75 thread.sleep(1000); 76 } 77 78 }
上一篇: 是她家开的