并发编程(7):线程之间的通信wait和notify

简介:

概念

  线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就成为整体的必用方式之一。当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同时还会使开发人员对线程任务在处理的过程中进行有效地把控与监督。


使用wait/notify方法实现线程间的通信,注意:

  1、wait和notify必须配合synchronized关键字使用

  2、wait方法释放锁,notify方法不释放锁

示例:

  当前两个线程t1,t2,当t1添加5个元素的时候,t2线程停止


例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public  class  Demo1 {
     @SuppressWarnings ( "rawtypes" )
     private  volatile  static  List list =  new  ArrayList();
     
     @SuppressWarnings ( "unchecked" )
     public  void  add() {
         list.add( "aaa" );
     }
     
     public  int  size() {
         return  list.size();
     }
     
     public  static  void  main(String[] args) {
         final  Demo1 demo1 =  new  Demo1();
         Thread t1 =  new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 try  {
                     for  ( int  i =  0 ; i <  10 ; i++) {
                         demo1.add();
                         System.out.println( "当前线程 : "  + Thread.currentThread().getName() +  "添加了一个元素" );
                         Thread.sleep( 500 );
                     }
                 catch  (Exception e) {
                     e.printStackTrace();
                 }
             }
         },  "t1" );
         
         Thread t2 =  new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 while ( true ) {
                     if  (demo1.size() ==  5 ) {
                         System.out.println( "当前线程收到通知 : "  + Thread.currentThread().getName() +  ", size = 5 线程停止..." );
                         throw  new  RuntimeException();
                     }
                 }
             }
         },  "t2" );
         t1.start();
         t2.start();
     }
}

效果:

    wKiom1jGl-ShF_G-AACwIKDxnjA659.png

这里t2线程中用的while(true)实现的,改进,如:使用wait/notify

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public  class  Demo2 {
     @SuppressWarnings ( "rawtypes" )
     private  volatile  static  List list =  new  ArrayList();
     
     @SuppressWarnings ( "unchecked" )
     public  void  add() {
         list.add( "aaa" );
     }
     
     public  int  size() {
         return  list.size();
     }
     
     public  static  void  main(String[] args) {
         final  Object lock =  new  Object();
         final  Demo2 demo2 =  new  Demo2();
         Thread t1 =  new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 try  {
                     synchronized  (lock) {
                         for  ( int  i =  0 ; i <  10 ; i++) {
                             demo2.add();
                             System.out.println( "当前线程 : "  + Thread.currentThread().getName() +  "添加了一个元素" );
                             Thread.sleep( 500 );
                             if  (demo2.size() ==  5 ) {
                                 System.out.println( "发出通知..." );
                                 lock.notify();
                             }
                         }
                     }
                 catch  (Exception e) {
                     e.printStackTrace();
                 }
             }
         },  "t1" );
         
         Thread t2 =  new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 try  {
                     synchronized  (lock) {
                         if  (demo2.size() !=  5 ) {
                             lock.wait();
                         }
                         System.out.println( "当前线程收到通知 : "  + Thread.currentThread().getName() +  ", size = 5 线程停止..." );
                         throw  new  RuntimeException();
                     }
                 catch  (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         },  "t2" );
         t2.start();
         t1.start();
     }
}

效果:

    wKiom1jGmymBnsE1AACowvkj3qg610.png

说明:在t1添加5条元素时,发出了通知,但是notify并没有释放锁,所以t2线程还不能执行。弊端就是不实时,使用CountDownLatch改进:await()/countDown()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public  class  Demo3 {
     @SuppressWarnings ( "rawtypes" )
     private  volatile  static  List list =  new  ArrayList();
     
     @SuppressWarnings ( "unchecked" )
     public  void  add() {
         list.add( "aaa" );
     }
     
     public  int  size() {
         return  list.size();
     }
     
     public  static  void  main(String[] args) {
         final  CountDownLatch cdl =  new  CountDownLatch( 1 );
         final  Demo3 demo2 =  new  Demo3();
         Thread t1 =  new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 try  {
                     for  ( int  i =  0 ; i <  10 ; i++) {
                         demo2.add();
                         System.out.println( "当前线程 : "  + Thread.currentThread().getName() +  "添加了一个元素" );
                         Thread.sleep( 500 );
                         if  (demo2.size() ==  5 ) {
                             System.out.println( "发出通知..." );
                             cdl.countDown();
                         }
                     }
                 catch  (Exception e) {
                     e.printStackTrace();
                 }
             }
         },  "t1" );
         
         Thread t2 =  new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 try  {
                     if  (demo2.size() !=  5 ) {
                         cdl.await();
                     }
                     System.out.println( "当前线程收到通知 : "  + Thread.currentThread().getName() +  ", size = 5 线程停止..." );
                     throw  new  RuntimeException();
                 catch  (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         },  "t2" );
         t2.start();
         t1.start();
     }
}

效果:

    wKioL1jGngXB46p9AACyvPR3Qm8100.png

使用wait和notify模拟queue

需求:

 模拟BlockingQueue:首先它是一个队列,并且支持阻塞的机制,阻塞的放入和得到数据,实现简单的方法put与take

 put(obj):

   把obj加到BlockingQueue里,如果BlockingQueue没有空间了,则调用此方法的线程被阻塞着,直到BlockingQueue里面有空间再继续。

 take():

   取走BlockingQueue里排在首位的数据,如BlockingQueue为空,则调用此方法的线程被阻塞着,直到BlockingQueue里面有数据再继续。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public  class  Demo4 {
     // 盛装元素的集合
     private  LinkedList<Object> list =  new  LinkedList<Object>();
     // 最小长度
     private  int  minSize =  0 ;
     
     // 长度
     AtomicInteger length =  new  AtomicInteger( 0 );
     
     // 最大长度
     private  final  int  maxSize;
     
     private  final  static  Object lock =  new  Object();
     
     public  Demo4( int  maxSize) {
         this .maxSize = maxSize;
     }
     
     // 添加元素
     public  void  put(Object obj) {
         synchronized  (lock) {
             if  (length.get() ==  this .maxSize) {
                 try  {
                     lock.wait();
                 catch  (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
             list.add(obj);
             length.incrementAndGet();
             System.out.println( "当前线程"  + Thread.currentThread().getName() +  "添加了一个元素 : "  + obj);
             lock.notifyAll();
         }
     }
     
     // 取出元素
     public  Object take() {
         Object obj =  null ;
         synchronized  (lock) {
             if  (length.get() ==  this .minSize) {
                 try  {
                     lock.wait();
                 catch  (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
             obj = list.removeFirst();
             length.decrementAndGet();
             System.out.println( "当前线程"  + Thread.currentThread().getName() +  "取出了一个元素 : "  + obj);
             lock.notifyAll();
         }
         return  obj;
     }
     
     public  static  void  main(String[] args) {
         final  Demo4 demo4 =  new  Demo4( 5 );
         demo4.put( "aa" );
         demo4.put( "bb" );
         demo4.put( "cc" );
         demo4.put( "ee" );
         
         new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 demo4.put( "ff" );
                 demo4.put( "gg" );
                 demo4.put( "hh" );
                 demo4.put( "ii" );
                 demo4.put( "jj" );
             }
         },  "t1" ).start();
         
         new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 demo4.take();
                 demo4.take();
             }
         },  "t2" ).start();
         
         new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 demo4.take();
                 demo4.take();
             }
         },  "t3" ).start();
     }
}


效果:

    wKiom1jGpSqD73UKAABZy-qKKUQ347.png

本文转自我爱大金子博客51CTO博客,原文链接http://blog.51cto.com/1754966750/1906113如需转载请自行联系原作者


我爱大金子

相关文章
|
14天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
1天前
|
Java
并发编程之线程池的底层原理的详细解析
并发编程之线程池的底层原理的详细解析
7 0
|
1天前
|
监控 Java
并发编程之线程池的详细解析
并发编程之线程池的详细解析
5 0
|
1天前
|
Java
并发编程之线程池的应用以及一些小细节的详细解析
并发编程之线程池的应用以及一些小细节的详细解析
11 0
|
5天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
5天前
|
安全 Java 程序员
Java中的多线程并发编程实践
【4月更文挑战第18天】在现代软件开发中,为了提高程序性能和响应速度,经常需要利用多线程技术来实现并发执行。本文将深入探讨Java语言中的多线程机制,包括线程的创建、启动、同步以及线程池的使用等关键技术点。我们将通过具体代码实例,分析多线程编程的优势与挑战,并提出一系列优化策略来确保多线程环境下的程序稳定性和性能。
|
11天前
|
Java 调度 开发者
Java 21时代的标志:虚拟线程带来的并发编程新境界
Java 21时代的标志:虚拟线程带来的并发编程新境界
19 0
|
15天前
|
Java Linux API
【并发编程】Java线程,如何创建线程
【并发编程】Java线程,如何创建线程
|
25天前
|
存储 缓存 NoSQL
Redis单线程已经很快了6.0引入多线程
Redis单线程已经很快了6.0引入多线程
31 3
|
28天前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
57 0

热门文章

最新文章

相关实验场景

更多