多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。
等待唤醒机制所涉及到的方法:
wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。
notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。
notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。
其实,所谓唤醒的意思就是让 线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。
仔细查看JavaAPI之后,发现这些方法 并不定义在 Thread中,也没定义在Runnable接口中,却被定义在了Object类中,为什么这些操作线程的方法定义在Object类中?
因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中。
01.public class Resource {
02. private String name;
03. private int count = 1;
04. private boolean flag ;
05.
06. public synchronized void set(String name){
07. if(flag){
08. try {
09. this.wait();
10. } catch (InterruptedException e) {
11. // TODO Auto-generated catch block
12. e.printStackTrace();
13. }
14. }
15. this.name = name + count;
16. count++;
17.
18. System.out.println(Thread.currentThread().getName()
19. + "The producer name is :++++++++++++++ " + this.name);
20.
21. flag = true;
22.
23. this.notify();
24.
25. }
26.
27. public synchronized void out(){
28. if(!flag){
29. try {
30. this.wait();
31. } catch (InterruptedException e) {
32. // TODO Auto-generated catch block
33. e.printStackTrace();
34. }
35. }
36. System.out.println(Thread.currentThread().getName() +
37. "The consumer name is : ---------------------------" + this.name);
38. flag = false;
39.
40. this.notify();
41.
42. }
43.
44.}
45.
46.public class Producer implements Runnable {
47.
48. private Resource r;
49. public Producer(Resource r){
50. this.r = r;
51. }
52. @Override
53. public void run() {
54.
55. while(true){
56. r.set("馒头");
57. }
58.
59. }
60.}
61.
62.public class Consumer implements Runnable {
63.
64. private Resource r;
65. public Consumer(Resource r ){
66. this.r = r;
67. }
68. @Override
69. public void run() {
70.
71. while(true){
72. r.out();
73. }
74. }
75.}
76.
77.public class ProConDemo {
78.
79. public static void main(String[] args) {
80.
81. //资源对象
82. Resource r = new Resource();
83. //任务对象
84. Producer pro = new Producer(r);
85. Consumer con = new Consumer(r);
86. //线程对象,两个生产者,两个消费者
87. Thread t1 = new Thread(pro);
88. Thread t2 = new Thread(pro);
89.
90. Thread t3 = new Thread(con);
91. Thread t4 = new Thread(con);
92.
93. //开启线程
94. t1.start();
95. t2.start();
96. t3.start();
97. t4.start();
98. }
99.
100.}
经过分析,发现,产生这种状况的根源是:
1,本方唤醒了本方。
2,被唤醒的本方没有判断标记。
为此,我们要做的改进是——将if 判断改为 while 标记,保证,每次被wait的线程在醒了之后,都得再次判断标记。进过修改,再次运行之后,会发现新的问题又来了,产生了死锁。经过分析,这个问题的根源在于,本方唤醒了本方,被唤醒的本方判断标记后,发现,标记不成立,就继续等待,因此,再也没有活着的线程。为此,我们需要唤醒,所有的线程,就用到了 notifyAll() 。将线程池中,所有等待的线程都叫醒。
经过修改后的代码成功的解决了上述的问题。
01.public class Resource {
02. private String name;
03. private int count = 1;
04. private boolean flag ;
05.
06. public synchronized void set(String name){
07. //用while 确保线程醒了后再次,判断标记。
08. while(flag){
09. try {
10. this.wait();
11. } catch (InterruptedException e) {
12. // TODO Auto-generated catch block
13. e.printStackTrace();
14. }
15. }
16. this.name = name + count;
17. count++;
18.
19. System.out.println(Thread.currentThread().getName()
20. + "The producer name is :++++++++++++++ " + this.name);
21.
22. flag = true;
23. //唤醒所有被等待的线程
24. this.notifyAll();
25.
26. }
27.
28. public synchronized void out(){
29. //用while 确保线程醒了后再次,判断标记。
30. while(!flag){
31. try {
32. this.wait();
33. } catch (InterruptedException e) {
34. // TODO Auto-generated catch block
35. e.printStackTrace();
36. }
37. }
38. System.out.println(Thread.currentThread().getName() +
39. "The consumer name is : ---------------------------" + this.name);
40. flag = false;
41. //唤醒所有被等待的线程
42. this.notifyAll();
43.
44. }
45.}
Lock:比同步函数和同步代码块要好一些。同步函数还是同步代码块所做的都是隐式的锁操作。并且,同步函数或者同步代码块使用的锁和监视器是同一个。
Lock接口:是将锁进行单独对象的封装。而且提供了对锁对象很多功能。 比如:lock()获取锁,unlock()释放锁。 Lock对锁的操作都是显示操作。所以它的出现要比同步函数或者同步代码块明确的多,更符合面向对象思想。
简单一句话:Lock接口的出现替代同步。
01.class Resource
02.{
03.
04. private String name;
05. private int count = 1;
06. private boolean flag;
07. //创建一个锁对象。
08. private final Lock lock = new ReentrantLock();
09. //创建一个生产者的监视器。
10. private Condition producer_con = lock.newCondition();
11. //创建一个消费者监视器。
12. private Condition consumer_con = lock.newCondition();
13.
14. public void set(String name)//
15. {
16. //获取锁。
17. lock.lock();
18. try
19. {
20.
21. while(flag)
22. try{producer_con.await();}catch(InterruptedException e){}
23. this.name = name + count;
24.
25. count++;
26.
27. System.out.println(Thread.currentThread().getName()+".....生产者......"+this.name);
28. flag = true;
29. consumer_con.signal();
30. }
31. finally
32. {
33. //释放锁。
34. lock.unlock();
35. }
36. }
37.
38. public void get()//
39. {
40. lock.lock();
41. try
42. {
43.
44. while(!flag)
45. try{consumer_con.await();}catch(InterruptedException e){}
46.
47. System.out.println(Thread.currentThread().getName()+".........消费者......"+this.name);
48. flag = false;
49. producer_con.signal();
50. }
51. finally
52. {
53. lock.unlock();
54. }
55. }
56.}