Java并发包——线程通信
摘要:本文主要学习了Java并发包里有关线程通信的一些知识。
部分内容来自以下博客:
https://www.cnblogs.com/skywang12345/p/3496716.html
线程通信方式
对于线程之间的通信方式,我们之前使用Object.wait()和Object.notify(),通过与synchronized关键字进行同步,两者配合使用,可以实现线程之间的通信。
后来在JUC并发包里发现可以使用Lock取代synchronized关键字实现线程之间的同步,并且使用Lock的方式有比synchronized方式更加强大的功能,为了同Lock配合,实现线程之间的通信,就要用到Condition。
Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait()、notify()、notifyAll()方法是和“同步锁”(synchronized关键字)捆绑使用的,而Condition是需要与“独享锁/共享锁”捆绑使用的。
使用Condition的优势
能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
能够在多个线程之间进行通信。Object的wait()、notify()、notifyAll()方法只能实现两个线程之间的通信,而Lock对象能通过newCondition()方法创建出无数的“条件”,通过这些条件,我们就能够成功地实现多线程之间的数据通信,对它们进行控制。
Condition
Condition是java.util.concurrent.locks包下的一个接口,提供了用来进行线程通信的方法。
1 public interface Condition { 2 // 使当前线程加入等待队列中并释放当锁,被通知、被中断时唤醒。 3 void await() throws InterruptedException; 4 5 // 同await()类似,只是该方法对中断不敏感,只有被通知时才被唤醒。 6 void awaitUninterruptibly(); 7 8 // 同await()类似,如果在指定时间之内没有被通知或者被中断,该方法会返回false。 9 boolean await(long time, TimeUnit unit) throws InterruptedException; 10 11 // 当前线程进入等待状态,被通知、中断或者超时之后被唤醒。返回值就是表示剩余的时间,超时返回值是0或者负数。 12 long awaitNanos(long nanosTimeout) throws InterruptedException; 13 14 // 同awaitNanos(long nanosTimeout)类似,只是参数变成了指定日期。 15 boolean awaitUntil(Date deadline) throws InterruptedException; 16 17 // 唤醒一个在等待队列中的线程。 18 void signal(); 19 20 // 唤醒所有在等待队列中的线程。 21 void signalAll(); 22 }
获取Lock上的特定Condition
Condition实例实质上被绑定到一个锁上。一个锁内部可以有多个Condition,即有多路等待和通知。要为特定Lock实例获得Condition实例,请使用Lock的newCondition()方法。
newCondition()返回用来与当前Lock实例一起使用的Condition实例。
类似于Object.wait()和Object.notify()的功能,Object.wait()与Object.notify()需要结合synchronized使用。Condition需要结合ReentrantLock使用。
使用Condition实现线程通信
使用synchronized和Object类的方法
使用synchronized和Object类的方法实现两个线程交替打印,代码如下:
1 public class Demo { 2 public static void main(String[] args) { 3 DemoThread demoThread = new DemoThread(); 4 Thread a = new Thread(demoThread, "线程A"); 5 Thread b = new Thread(demoThread, "线程B"); 6 a.start(); 7 b.start(); 8 } 9 } 10 11 class DemoThread implements Runnable { 12 private Integer num = 1; 13 14 @Override 15 public void run() { 16 synchronized (DemoThread.class) { 17 while (num <= 10) { 18 DemoThread.class.notify(); 19 System.out.println(Thread.currentThread().getName() + " >>> " + num++); 20 if (num <= 10) { 21 try { 22 DemoThread.class.wait(); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 } 27 } 28 } 29 } 30 }
运行结果如下:
1 线程A >>> 1 2 线程B >>> 2 3 线程A >>> 3 4 线程B >>> 4 5 线程A >>> 5 6 线程B >>> 6 7 线程A >>> 7 8 线程B >>> 8 9 线程A >>> 9 10 线程B >>> 10
使用Lock和Condition类的方法
使用Lock和Condition类的方法实现两个线程交替打印,代码如下:
1 public class Demo { 2 public static void main(String[] args) { 3 DemoThread demoThread = new DemoThread(); 4 Thread a = new Thread(demoThread, "线程A"); 5 Thread b = new Thread(demoThread, "线程B"); 6 a.start(); 7 b.start(); 8 } 9 } 10 11 class DemoThread implements Runnable { 12 private Integer num = 1; 13 Lock lock = new ReentrantLock(); 14 Condition condition = lock.newCondition(); 15 16 @Override 17 public void run() { 18 lock.lock(); 19 try { 20 while (num <= 10) { 21 condition.signal(); 22 System.out.println(Thread.currentThread().getName() + " >>> " + num++); 23 if (num <= 10) { 24 condition.await(); 25 } 26 } 27 } catch (Exception e) { 28 e.printStackTrace(); 29 } finally { 30 lock.unlock(); 31 } 32 } 33 }
运行结果如下:
1 线程A >>> 1 2 线程B >>> 2 3 线程A >>> 3 4 线程B >>> 4 5 线程A >>> 5 6 线程B >>> 6 7 线程A >>> 7 8 线程B >>> 8 9 线程A >>> 9 10 线程B >>> 10
使用Lock和Condition类的方法实现三个线程按顺序循环打印
使用Lock和Condition类的方法实现三个线程按顺序打印,需要唤醒指定的线程,代码如下:
1 public class Demo { 2 public static void main(String[] args) { 3 DemoThread demoThread = new DemoThread(); 4 Thread a = new Thread(() -> demoThread.run1(), "线程1"); 5 Thread b = new Thread(() -> demoThread.run2(), "线程2"); 6 Thread c = new Thread(() -> demoThread.run3(), "线程3"); 7 a.start(); 8 b.start(); 9 c.start(); 10 } 11 } 12 13 class DemoThread { 14 static private Integer num = 0; 15 static Lock lock = new ReentrantLock(); 16 Condition c1 = lock.newCondition(); 17 Condition c2 = lock.newCondition(); 18 Condition c3 = lock.newCondition(); 19 20 public void run1() { 21 try { 22 Thread.sleep(10); 23 } catch (InterruptedException e1) { 24 e1.printStackTrace(); 25 } 26 lock.lock(); 27 try { 28 while (num < 10) { 29 for (int i = 0; i < 1 && num < 10; i++) { 30 System.out.println(Thread.currentThread().getName() + " >>> " + num); 31 } 32 num++; 33 c2.signal(); 34 c1.await(); 35 if (num >= 9) { 36 c3.signal(); 37 } 38 } 39 } catch (Exception e) { 40 e.printStackTrace(); 41 } finally { 42 lock.unlock(); 43 } 44 } 45 46 public void run2() { 47 try { 48 Thread.sleep(20); 49 } catch (InterruptedException e1) { 50 e1.printStackTrace(); 51 } 52 lock.lock(); 53 try { 54 while (num < 10) { 55 for (int i = 0; i < 2 && num < 10; i++) { 56 System.out.println(Thread.currentThread().getName() + " >>> " + num); 57 } 58 num++; 59 c3.signal(); 60 c2.await(); 61 if (num >= 9) { 62 c1.signal(); 63 } 64 } 65 } catch (Exception e) { 66 e.printStackTrace(); 67 } finally { 68 lock.unlock(); 69 } 70 } 71 72 public void run3() { 73 try { 74 Thread.sleep(30); 75 } catch (InterruptedException e1) { 76 e1.printStackTrace(); 77 } 78 lock.lock(); 79 try { 80 while (num < 10) { 81 for (int i = 0; i < 3 && num < 10; i++) { 82 System.out.println(Thread.currentThread().getName() + " >>> " + num); 83 } 84 num++; 85 c1.signal(); 86 c3.await(); 87 if (num >= 9) { 88 c2.signal(); 89 } 90 } 91 } catch (Exception e) { 92 e.printStackTrace(); 93 } finally { 94 lock.unlock(); 95 } 96 } 97 }
运行结果如下:
1 线程1 >>> 0 2 线程2 >>> 1 3 线程2 >>> 1 4 线程3 >>> 2 5 线程3 >>> 2 6 线程3 >>> 2 7 线程1 >>> 3 8 线程2 >>> 4 9 线程2 >>> 4 10 线程3 >>> 5 11 线程3 >>> 5 12 线程3 >>> 5 13 线程1 >>> 6 14 线程2 >>> 7 15 线程2 >>> 7 16 线程3 >>> 8 17 线程3 >>> 8 18 线程3 >>> 8 19 线程1 >>> 9
Condition中的await()、signal()、signalAll()与Object中的wait()、notify()、notifyAll()区别
使用方式不同
Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。
不同的是,Object中的这些方法是和同步锁捆绑使用的,而Condition是需要与互斥锁/共享锁捆绑使用的。
功能更加强大
Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒“读线程”。当从缓冲区读出数据之后,唤醒“写线程”。当缓冲区满的时候,“写线程”需要等待。当缓冲区为空时,“读线程”需要等待。
如果采用Object类中的wait()、notify()、notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒“读线程”时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程",而只能通过notifyAll唤醒所有线程,但是notifyAll无法区分唤醒的线程是读线程,还是写线程。
但是,通过Condition,就能明确的指定唤醒读线程。