《day15---多线程安全问题_JDK1.5的锁机制》

 1 //15同步问题的分析案例以及解决思路
 2 
 3 //两个客户到一个银行去存钱,每个客户一次存100,存3次。
 4 //问题,该程序是否有安全问题,如果有,写出分析过程,并定于解决方案。
 5 
 6 /*
 7 发现运行结果:
 8 sum=200
 9 sum=200
10 sum=300
11 sum=400
12 sum=600
13 sum=500
14 
15 打印错乱,不关心,但是发现数值错误,没有100.
16 运行了几次,发现有对的。
17 
18 
19 
20 说明多线程的随机性造成了安全问题。
21 哪的问题?
22 1,既然是多线程的问题,必须问题发生在多线程中
23 2,任务代码中是否有共性数据呢?b对象中的sum.
24 3,是否有对sum进行多次运算呢?有!
25 
26 加同步就行了。
27 */
28 //描述银行,
29 class Bank
30 {
31     private int sum;
32     private Object obj = new Object();
33     public void add(int num)
34     {
35         synchronized(obj)
36         {
37             sum = sum+num;
38             System.out.println("sum="+sum);//每存一次,看到银行金额变化。
39         }
40     }
41 }
42 
43 class Consumer implements Runnable
44 {
45     private Bank b = new Bank();
46     public void run()
47     {
48         for(int x=0;x<3;x++)
49         {
50             b.add(100);//一次存100,循环3次。
51         }
52     }
53 }
54 class ThreadTest
55 {
56     public static void main(String[] args) 
57     {
58         Consumer c = new Consumer();
59         Thread t1 = new Thread(c);
60         Thread t2 = new Thread(c);
61         t1.start();
62         t2.start();
63     }
64 }
 1 //同步函数相关问题,以及同步函数和同步代码块的比较
 2 /*
 3 同步的另一种体现形式。同步函数。
 4 
 5 同步函数使用的锁是哪个?
 6 经过简单的分析:大概猜的是this。因为函数必须被对象调用。
 7 
 8 验证:
 9 写一个同步代码块,写一个同步函数。如果同步代码块中的锁对象和同步函数中的锁对象
10 是同一个,就同步了,就没有错误的数据了,如果不是同一个锁对象,就不同步,会出现错误数据,
11 
12 让两个线程,一个线程在同步代码块中执行,一个线程待同步函数中执行。
13 
14 总结:同步函数使用的锁是this.
15 
16 同步函数和同步代码块的的区别?
17 1,同步函数使用的锁是固定的this。当线程任务只需要一个同步时,完全可以通过同步函数来体现。
18 2,同步代码块使用的是锁可以是任意对象。当线程任务需要多个同步时,必须通过所来区分。这时必须使用同步代码块。
19 同步代码块较为常用。
20 
21 如果只用一个同步时,完全可以简化为
22 */
23 
24 class Ticket implements Runnable
25 {
26     //1,描述票的数量
27     private int tickets = 100;
28 
29     //2,售票的动作。这个动作需要被多线程执行,那就是线程任务代码,
30     //需要定义在run方法中。
31     //记住,线程任务中通常都有循环结构。
32     //private Object obj = new Object();
33     boolean flag = true;
34     public void run()
35     {
36         if(flag){
37         while(true)
38             {
39                 synchronized(this)
40                 {
41                     if(tickets > 0)
42                     {
43                         try{Thread.sleep(1);}catch(InterruptedException e){/*未写处理方式,后面讲*/}
44                         System.out.println(Thread.currentThread().getName()+"..obj.."+tickets--);//打印线程名称
45                     }
46                 }
47             }
48         }
49         
50         else{
51             while(true)
52             {
53                 this.sale();
54             }
55         }
56     }
57 
58     public synchronized void sale()//同步函数。具备了同步性的函数,使用的锁对象就是this
59     {
60         if(tickets > 0)
61         {
62             //要让线程在这里稍停,模拟问题的发生。sleep 看到了 0 -1 -2 这样的错误的数据,这就是传说中的安全问题。
63             try{Thread.sleep(1);}catch(InterruptedException e){/*未写处理方式,后面讲*/}
64             System.out.println(Thread.currentThread().getName()+"..sale.."+tickets--);//打印线程名称
65         }
66     }
67 }
68 
69 
70 class ThreadDemo4
71 {
72     public void main(String[] args) 
73     {
74         //1,创建Runnable接口的子类对象。
75         Ticket t = new Ticket();
76 
77         //创建四个线程对象。并将Runnable接口的子类对象作为参数传递给Thread的构造函数。
78         Thread t1 = new Thread(t);
79         Thread t2 = new Thread(t);
80 
81         //3,开启四个线程
82         t1.start();
83         
84         try{Thread.sleep(10);}catch(InterruptedException e){}
85         //切换标记;之前,让主线程停一会儿,这时就只有一个t1线程在。
86         t.flag = false;
87 
88         t2.start();
89     }
90 }
 1 /*
 2 静态同步函数使用的锁不是this.而是字节码文件对象。类名.class
 3 */
 4 
 5 class Ticket implements Runnable
 6 {
 7     //1,描述票的数量
 8     private static int tickets = 100;
 9 
10     //2,售票的动作。这个动作需要被多线程执行,那就是线程任务代码,
11     //需要定义在run方法中。
12     //记住,线程任务中通常都有循环结构。
13     //private Object obj = new Object();
14     boolean flag = true;
15     public void run()
16     {
17         if(flag){
18         while(true)
19             {
20                 synchronized(Ticket.class)
21                 {
22                     if(tickets > 0)
23                     {
24                         try{Thread.sleep(1);}catch(InterruptedException e){/*未写处理方式,后面讲*/}
25                         System.out.println(Thread.currentThread().getName()+"..obj.."+tickets--);//打印线程名称
26                     }
27                 }
28             }
29         }
30         
31         else{
32             while(true)
33             {
34                 this.sale();
35             }
36         }
37     }
38 
39     public static synchronized void sale()//加了静态后,已经知道静态函数里面没有了this.
40     {
41         if(tickets > 0)
42         {
43             //要让线程在这里稍停,模拟问题的发生。sleep 看到了 0 -1 -2 这样的错误的数据,这就是传说中的安全问题。
44             try{Thread.sleep(1);}catch(InterruptedException e){/*未写处理方式,后面讲*/}
45             System.out.println(Thread.currentThread().getName()+"..sale.."+tickets--);//打印线程名称
46         }
47     }
48 }
49 
50 
51 class ThreadDemo5
52 {
53     public static void main(String[] args) 
54     {
55         //1,创建Runnable接口的子类对象。
56         Ticket t = new Ticket();
57 
58         //创建四个线程对象。并将Runnable接口的子类对象作为参数传递给Thread的构造函数。
59         Thread t1 = new Thread(t);
60         Thread t2 = new Thread(t);
61 
62         //3,开启四个线程
63         t1.start();
64         
65         try{Thread.sleep(10);}catch(InterruptedException e){}
66         //切换标记;之前,让主线程停一会儿,这时就只有一个t1线程在。
67         t.flag = false;
68 
69         t2.start();
70     }
71 }
 1 //单例懒汉模式的并发访问相关问题
 2 /*
 3 单例懒汉模式的并发访问相关问题
 4 */
 5 
 6 //饿汉式;多线程并发没问题。
 7 class Single
 8 {
 9     private static final Single s = new Single();
10 
11     private Single(){}
12 
13     public static Single getInstance()
14     {
15         return s;
16     }
17 }
18 
19 //懒汉式:
20 class Single
21 {
22     private static  Single s = null;
23 
24     private Single(){}
25     /*
26     并发访问,会有安全隐患,所以加入同步机制解决安全问题。
27     但是,同步的出现却降低了效率。(提稿效率的办法就是减少判断锁的次数)
28     可以通过双重判断的方式,解决效率问题,减少判断的次数。
29 
30     */
31     public static /*synchronized*/ Single getInstance()
32     {
33         if(s==null)//只要有一个线程把对象创建完,其他线程就再也不会判断锁了。
34         {
35             synchronized(Single.class)
36             {
37                 if(s==null)
38         //            --->0  --->1
39                     s = new Single();
40             }
41         }
42         return s;
43     }
44 }
45 class Demo implements Runnable
46 {
47     public void run()
48     {
49         Single.getInstance();
50     }
51 }
52 
53 class ThreadDemo6 
54 {
55     public static void main(String[] args) 
56     {
57         System.out.println("Hello World!");
58     }
59 }
  1 //同步的另一个弊端。死锁
  2 /*
  3 同步的另一个弊端。
  4 
  5 情况之一:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。
  6 这时容易引发一种现象,死锁。
  7 这种情况能避免就避免。
  8 
  9 //Thread-0
 10 synchronized(obj1)
 11 {
 12     --->thread-0 obj1
 13     synchronized(obj2)
 14     {
 15 
 16     }
 17 }
 18 
 19 //Thread-1
 20 synchronized(obj2)
 21 {
 22     thread-1 obj2
 23     synchronized(obj1)
 24     {
 25 
 26     }
 27 }
 28 
 29 */
 30 
 31 class Ticket implements Runnable
 32 {
 33     //1,描述票的数量
 34     private int tickets = 100;
 35 
 36     //2,售票的动作。这个动作需要被多线程执行,那就是线程任务代码,
 37     //需要定义在run方法中。
 38     //记住,线程任务中通常都有循环结构。
 39     private Object obj = new Object();
 40     boolean flag = true;
 41     public void run()
 42     {
 43         if(flag){
 44         while(true)
 45             {
 46                 synchronized(obj)//obj锁
 47                 {
 48                     sale();//this锁
 49                 }
 50             }
 51         }
 52         
 53         else{
 54             while(true)
 55             {
 56                 this.sale();
 57             }
 58         }
 59     }
 60 
 61     public  synchronized void sale()//this锁
 62     {
 63         synchronized(obj)//obj锁
 64         {
 65         if(tickets > 0)
 66             {
 67                 //要让线程在这里稍停,模拟问题的发生。sleep 看到了 0 -1 -2 这样的错误的数据,这就是传说中的安全问题。
 68                 try{Thread.sleep(1);}catch(InterruptedException e){/*未写处理方式,后面讲*/}
 69                 System.out.println(Thread.currentThread().getName()+"..sale.."+tickets--);//打印线程名称
 70             }
 71         }
 72     }
 73 }
 74 
 75 
 76 class ThreadDemo7
 77 {
 78     public static void main(String[] args) 
 79     {
 80         //1,创建Runnable接口的子类对象。
 81         Ticket t = new Ticket();
 82 
 83         //创建四个线程对象。并将Runnable接口的子类对象作为参数传递给Thread的构造函数。
 84         Thread t1 = new Thread(t);
 85         Thread t2 = new Thread(t);
 86 
 87         //3,开启四个线程
 88         t1.start();
 89         
 90         try{Thread.sleep(10);}catch(InterruptedException e){}
 91         //切换标记;之前,让主线程停一会儿,这时就只有一个t1线程在。
 92         t.flag = false;
 93 
 94         t2.start();
 95     }
 96 }
 97 
 98 //这个程序在运行过程中死锁了。
 99 /*
100 Thread-0..sale..100
101 Thread-0..sale..99
102 Thread-0..sale..98
103 Thread-0..sale..97
104 Thread-0..sale..96
105 Thread-0..sale..95
106 */
 1 //死锁示例--面试会用到
 2 class Test implements Runnable
 3 {
 4     private boolean flag;
 5     Test(boolean flag)
 6     {
 7         this.flag = flag;
 8     }
 9     public void run()
10     {
11         if(flag)
12         {
13             while(true)
14             {
15                 synchronized(MyLock.LOCKA)
16                 {
17                     System.out.println(Thread.currentThread().getName()+"if......locka");
18                     synchronized(MyLock.LOCKB)
19                     {
20                         System.out.println(Thread.currentThread().getName()+"if......lockb");
21                     }
22                 }
23             }
24         }
25         else
26         {
27             while(true)
28             {
29                 synchronized(MyLock.LOCKB)
30                 {
31                     System.out.println(Thread.currentThread().getName()+"else......lockb");
32                     synchronized(MyLock.LOCKA)
33                     {
34                         System.out.println(Thread.currentThread().getName()+"else......locka");
35                     }
36                 }
37             }
38         }
39     }
40 }
41 
42 //单独定义一个用于存储锁对象类。
43 class MyLock
44 {
45     public static final Object LOCKA = new Object();
46     public static final Object LOCKB = new Object();
47 }
48 class DeadLockTest
49 {
50     public static void main(String[] args) 
51     {
52         //创建了两个线程任务。
53         Test t1 = new Test(true);
54         Test t2 = new Test(false);
55 
56         Thread t11 = new Thread(t1);
57         Thread t22 = new Thread(t2);
58         t11.start();
59         t22.start();
60     }
61 }
  1 //多线程间的通信-生产者&消费者-问题发生。
  2 /*
  3 多线程中最为常见的应用案例,
  4 生产者消费者问题。
  5 生产和消费同时执行,需要多线程。
  6 但是执行的任务却不相同,处理的资源是相同的。线程间的通信。
  7 
  8 1,描述资源。
  9 2,描述生产者,因为具备着自己的任务。
 10 3,描述消费者,因为具备着自己的任务。
 11 
 12 问题1:
 13 数据错误,已经被生产很早期的商品,才被消费到。
 14 出现线程安全问题,需要用同步来解决。
 15 问题已解决:不会再消费到之前很早期的商品。
 16 
 17 问题2:
 18 发现了连续生产却没有消费,同时对同一个商品进行多次消费。
 19 希望的结果应该是生产一个商品,就被消费掉,生产下一个商品。
 20 
 21 搞清楚机个问题:
 22 生产者什么时候应该生产呢?
 23 当盘子中没有面包,就生产,如果有了面包,就不要消费。
 24 
 25 消费者什么时候应该消费呢?
 26 当盘子中已有面包,就消费,如果没有面包,就不要消费。
 27 
 28 */
 29 
 30 //1,描述资源。属性:名称和编号。    行为:对商品名称赋值,获取商品
 31 class Resource
 32 {
 33     private String name;
 34     private int count=1;
 35 
 36     //1,提供设置的方法。
 37     public synchronized void set(String name)
 38     {
 39         //1,给成员变量赋值并加上编号。
 40         this.name = name+count;
 41         count++;
 42 
 43         //2,打印生产了哪个商品。
 44         System.out.println(Thread.currentThread().getName()+"....生产者..."+this.name);
 45 
 46     }
 47 
 48     public synchronized void out()
 49     {
 50         System.out.println(Thread.currentThread().getName()+"....消费者..."+this.name);
 51     }
 52 }
 53 
 54 //2,描述生产者。
 55 class Producer implements Runnable
 56 {
 57     private Resource r ;
 58     //生产者一初始化就要有资源。需要将资源传递到构造函数中。
 59     Producer(Resource r)
 60     {
 61         this.r = r;
 62     }
 63     public void run()
 64     {
 65         while(true)
 66         {
 67             r.set("面包");
 68         }
 69     }
 70 }
 71 
 72 //3,描述消费者。
 73 class Consumer implements Runnable
 74 {
 75     private Resource r ;
 76     //消费者一初始化就要有资源。需要将资源传递到构造函数中。
 77     Consumer(Resource r)
 78     {
 79         this.r = r;
 80     }
 81     public void run()
 82     {
 83         while(true)
 84         {
 85             r.out();
 86         }
 87     }
 88 }
 89 
 90 class ThreadDemo8
 91 {
 92     public static void main(String[] args) 
 93     {
 94         //创建资源对象。
 95         Resource r = new Resource();
 96 
 97         //创建线程任务。
 98         Producer pro = new Producer(r);
 99         Consumer con = new Consumer(r);
100 
101         //3,创建线程。
102         Thread t1 = new Thread(pro);
103         Thread t2 = new Thread(con);
104 
105         t1.start();
106         t2.start();
107     }
108 }
  1 //多线程间的通信-生产者&消费者-问题发生。以及等待唤醒机制。
  2 /*
  3 多线程中最为常见的应用案例,
  4 生产者消费者问题。
  5 生产和消费同时执行,需要多线程。
  6 但是执行的任务却不相同,处理的资源是相同的。线程间的通信。
  7 
  8 1,描述资源。
  9 2,描述生产者,因为具备着自己的任务。
 10 3,描述消费者,因为具备着自己的任务。
 11 
 12 问题1:
 13 数据错误,已经被生产很早期的商品,才被消费到。
 14 出现线程安全问题,需要用同步来解决。
 15 问题已解决:不会再消费到之前很早期的商品。
 16 
 17 问题2:
 18 发现了连续生产却没有消费,同时对同一个商品进行多次消费。
 19 希望的结果应该是生产一个商品,就被消费掉,生产下一个商品。
 20 
 21 搞清楚机个问题:
 22 生产者什么时候应该生产呢?
 23 当盘子中没有面包,就生产,如果有了面包,就不要消费。
 24 
 25 消费者什么时候应该消费呢?
 26 当盘子中已有面包,就消费,如果没有面包,就不要消费。
 27 
 28 生产者生产了商品后,应该去告诉消费者来消费。而这时的生产者应该处于等待状态。
 29 消费者消费了商品后,应该告诉生产者去生产,而这时的消费者应该处于等待状态。
 30 
 31 
 32 ======================================================
 33 等待/唤醒机制。
 34 wait();会让线程处于等待状态,其实就是将线程临时存储到了线程池中。
 35 notify();会唤醒线程池中任意一个等待的线程。
 36 notifyAll();会唤醒线程池中所有的等待线程。
 37 
 38 记住:这些方法必须使用在同步中,必须要标识wait,notify等方法所属的锁。
 39 同一个锁上的notify,只能唤醒该锁上的wait线程。
 40 
 41 为什么这些方法定义在了Object中呢?
 42 因为这些方法必须标识所属的锁。而锁可以是任意对象,任意对象可以调用的方法必然是Objec的方法。
 43 
 44 
 45 距离:小朋友抓人游戏。
 46 */
 47 
 48 //1,描述资源。属性:名称和编号。    行为:对商品名称赋值,获取商品
 49 class Resource
 50 {
 51     private String name;
 52     private int count=1;
 53 
 54     //定义标记。
 55     private boolean flag = false;
 56 
 57     //1,提供设置的方法。
 58     public synchronized void set(String name)
 59     {
 60         if(flag)
 61             try{this.wait();}catch(InterruptedException e){}
 62         //1,给成员变量赋值并加上编号。
 63         this.name = name+count;
 64         count++;
 65 
 66         //2,打印生产了哪个商品。
 67         System.out.println(Thread.currentThread().getName()+"....生产者..."+this.name);
 68         
 69         //将标记该为true
 70         flag = true;
 71         //唤醒消费者。
 72         this.notify();
 73 
 74     }
 75 
 76     public synchronized void out()
 77     {
 78         if(!flag)
 79             try{wait();}catch(InterruptedException e){}
 80         System.out.println(Thread.currentThread().getName()+"....消费者..."+this.name);
 81         //将标记该为flase。
 82         flag = false;
 83         //唤醒生产者。
 84         notify();
 85     }
 86 }
 87 
 88 //2,描述生产者。
 89 class Producer implements Runnable
 90 {
 91     private Resource r ;
 92     //生产者一初始化就要有资源。需要将资源传递到构造函数中。
 93     Producer(Resource r)
 94     {
 95         this.r = r;
 96     }
 97     public void run()
 98     {
 99         while(true)
100         {
101             r.set("面包");
102         }
103     }
104 }
105 
106 //3,描述消费者。
107 class Consumer implements Runnable
108 {
109     private Resource r ;
110     //消费者一初始化就要有资源。需要将资源传递到构造函数中。
111     Consumer(Resource r)
112     {
113         this.r = r;
114     }
115     public void run()
116     {
117         while(true)
118         {
119             r.out();
120         }
121     }
122 }
123 
124 class ThreadDemo9
125 {
126     public static void main(String[] args) 
127     {
128         //创建资源对象。
129         Resource r = new Resource();
130 
131         //创建线程任务。
132         Producer pro = new Producer(r);
133         Consumer con = new Consumer(r);
134 
135         //3,创建线程。
136         Thread t1 = new Thread(pro);
137         Thread t2 = new Thread(con);
138 
139         t1.start();
140         t2.start();
141     }
142 }
  1 //多生产&多消费问题发生及解决。
  2 /*
  3 java.util.concurrent.locks 软件包中提供了相应的解决方案。
  4 Lock接口。比同步更厉害,有更多的操作,获取锁:lock(); 释放锁:unlock();
  5           提供了一个更加面向对象的锁。在该锁中提供了更多的显示的锁操作。
  6           可以替代同步。
  7 
  8 升级到JDK1.5,先别同步改为Lock.
  9 */
 10 
 11 /*
 12 多生产多消费。
 13 问题1:生产了商品没有被消费,同一个商品被消费多次。
 14 Thread-2....生产者...面包285
 15 Thread-0....生产者...面包286
 16 Thread-1....消费者...面包286
 17 Thread-2....生产者...面包287
 18 Thread-0....生产者...面包288
 19 Thread-1....消费者...面包288
 20 
 21 被唤醒的线程没有判断标记,造成了问题1的产生。
 22 解决:只要让被唤醒的线程必须判断标记就可以了。将if判断标记的方式改为while判断标记。
 23 注意:只要是多生产,多消费,必须是while判断标记。
 24 
 25 问题2:while判断后,死锁了。
 26 原因:生产方唤醒了线程池中的生产方的线程。本方唤醒了本方。
 27 解决:希望本方要唤醒对方。没有对应方法,只能唤醒所有。
 28 
 29 其实还是有一些问题的。效率低了。
 30 */
 31 //1,描述资源。属性:名称和编号。    行为:对商品名称赋值,获取商品
 32 class Resource
 33 {
 34     private String name;
 35     private int count=1;
 36 
 37     //定义一个锁对象。
 38     private Lock lock = new ReentrantLock();
 39 
 40     //定义标记。
 41     private boolean flag = false;
 42 
 43     //1,提供设置的方法。
 44     public synchronized void set(String name)// t1 t2
 45     {
 46         //if(flag)
 47         while(flag)
 48             try{this.wait();}catch(InterruptedException e){}
 49         //1,给成员变量赋值并加上编号。
 50         this.name = name+count;//商品1
 51         count++;//2
 52         //2,打印生产了哪个商品。
 53         System.out.println(Thread.currentThread().getName()+"....生产者..."+this.name);//生产了商品1
 54         
 55         //将标记该为true
 56         flag = true;
 57         //唤醒消费者。
 58         this.notifyAll();
 59 
 60     }
 61 
 62     public synchronized void out()// t3 t4
 63     {
 64         //if(!flag)
 65         while(!flag)
 66             try{wait();}catch(InterruptedException e){}
 67         System.out.println(Thread.currentThread().getName()+"....消费者..."+this.name);
 68         //将标记该为flase。
 69         flag = false;
 70         //唤醒生产者。
 71         this.notifyAll();
 72     }
 73 }
 74 
 75 //2,描述生产者。
 76 class Producer implements Runnable
 77 {
 78     private Resource r ;
 79     //生产者一初始化就要有资源。需要将资源传递到构造函数中。
 80     Producer(Resource r)
 81     {
 82         this.r = r;
 83     }
 84     public void run()
 85     {
 86         while(true)
 87         {
 88             r.set("面包");
 89         }
 90     }
 91 }
 92 
 93 //3,描述消费者。
 94 class Consumer implements Runnable
 95 {
 96     private Resource r ;
 97     //消费者一初始化就要有资源。需要将资源传递到构造函数中。
 98     Consumer(Resource r)
 99     {
100         this.r = r;
101     }
102     public void run()
103     {
104         while(true)
105         {
106             r.out();
107         }
108     }
109 }
110 
111 class ThreadDemo10
112 {
113     public static void main(String[] args) 
114     {
115         //创建资源对象。
116         Resource r = new Resource();
117 
118         //创建线程任务。
119         Producer pro = new Producer(r);
120         Consumer con = new Consumer(r);
121 
122         //3,创建线程。
123         Thread t1 = new Thread(pro);
124         Thread t2 = new Thread(pro);
125         Thread t3 = new Thread(con);
126         Thread t4 = new Thread(con);
127 
128         t1.start();
129         t2.start();
130         t3.start();
131         t4.start();
132     }
133 }
  1 //多生产&多消费问题发生及解决。
  2 import java.util.concurrent.locks.*;
  3 /*
  4 java.util.concurrent.locks 软件包中提供了相应的解决方案。
  5 Lock接口。比同步更厉害,有更多的操作,获取锁:lock(); 释放锁:unlock();
  6           提供了一个更加面向对象的锁。在该锁中提供了更多的显示的锁操作。
  7           可以替代同步。
  8 
  9 升级到JDK1.5,先别同步改为Lock.
 10 已经将旧锁替换成新锁,那么所上的监视器方法(wait notify notifyAll )也应该替换
 11 成新锁上的方法。
 12 而JDK1.5中将这些原有的监视器方法封装到了一个Condition对象中。
 13 想要获取监视器方法,需要先获取Condition对象。
 14 
 15 Condition对象的出现其实就是替代了Object中的监视器方法。
 16 await();
 17 signal();
 18 signalAll();
 19 将所有的监视器方法替换成了Condition.
 20 功能和ThreadDemo10.java的程序一样,仅仅是用新的对象,改了写法而已。
 21 但是问题依旧,效率还是很低。
 22 老程序中可以通过两个嵌套完成,但是容易引发死锁。
 23 
 24 新程序中,就可以解决这个问题。
 25 可以在一个锁上加上多个监视器对象。
 26 */
 27 class Resource
 28 {
 29     private String name;
 30     private int count=1;
 31 
 32     //定义一个锁对象。
 33     private Lock lock = new ReentrantLock();
 34     //获取锁上的Condition对象。为了解决本方唤醒对方的问题,可以一个锁上创建两个监视器对象。
 35     private Condition produce = lock.newCondition();//负责生产的.
 36     private Condition consume = lock.newCondition();//负责消费的.
 37 
 38 
 39     //定义标记。
 40     private boolean flag = false;
 41 
 42     //1,提供设置的方法。
 43     public void set(String name)// t1 t2
 44     {
 45         //获取锁。
 46         lock.lock();
 47         try{
 48         while(flag)
 49             try{produce.await();}catch(InterruptedException e){}
 50         this.name = name+count;//商品1
 51         count++;
 52         System.out.println(Thread.currentThread().getName()+"....生产者..."+this.name);//生产了商品1
 53         
 54         //将标记该为true
 55         flag = true;
 56         //执行消费者的唤醒。而且是唤醒一个消费者就行了。
 57         consume.signal();
 58         }
 59         finally{
 60             lock.unlock();//一定要执行。
 61         }
 62     }
 63 
 64     public void out()// t3 t4
 65     {
 66         lock.lock();
 67         try{
 68         while(!flag)
 69             try{consume.await();}catch(InterruptedException e){}
 70         System.out.println(Thread.currentThread().getName()+"....消费者..."+this.name);
 71         //将标记该为flase。
 72         flag = false;
 73         produce.signal();
 74         }
 75         finally{
 76             lock.unlock();
 77         }
 78     }
 79 }
 80 
 81 //2,描述生产者。
 82 class Producer implements Runnable
 83 {
 84     private Resource r ;
 85     //生产者一初始化就要有资源。需要将资源传递到构造函数中。
 86     Producer(Resource r)
 87     {
 88         this.r = r;
 89     }
 90     public void run()
 91     {
 92         while(true)
 93         {
 94             r.set("面包");
 95         }
 96     }
 97 }
 98 
 99 //3,描述消费者。
100 class Consumer implements Runnable
101 {
102     private Resource r ;
103     //消费者一初始化就要有资源。需要将资源传递到构造函数中。
104     Consumer(Resource r)
105     {
106         this.r = r;
107     }
108     public void run()
109     {
110         while(true)
111         {
112             r.out();
113         }
114     }
115 }
116 
117 class ThreadDemo11
118 {
119     public static void main(String[] args) 
120     {
121         //创建资源对象。
122         Resource r = new Resource();
123 
124         //创建线程任务。
125         Producer pro = new Producer(r);
126         Consumer con = new Consumer(r);
127 
128         //3,创建线程。
129         Thread t1 = new Thread(pro);
130         Thread t2 = new Thread(pro);
131         Thread t3 = new Thread(con);
132         Thread t4 = new Thread(con);
133 
134         t1.start();
135         t2.start();
136         t3.start();
137         t4.start();
138     }
139 }
 1 //接口 Condition示例:
 2 class BoundedBuffer {
 3    final Lock lock = new ReentrantLock();//
 4    final Condition notFull  = lock.newCondition(); //生产。
 5    final Condition notEmpty = lock.newCondition(); //消费。
 6 
 7    final Object[] items = new Object[100];//存储商品的容器。
 8    int putptr/*生产者使用的脚标*/, takeptr,/*消费者使用的角标*/ count;//计数器。
 9 
10     /*生产者使用的方法,往数组中存储商品。*/
11    public void put(Object x) throws InterruptedException {
12      lock.lock();
13      try {
14        while (count == items.length) //判断计数器是否已到数据长度。慢了。
15          notFull.await();//生产者等待。
16 
17        items[putptr] = x; //安照角标将商品存储到数组中。
18 
19        if (++putptr == items.length)//如果存储的角标到了数组的长度,就将角标归零。
20            putptr = 0;
21        ++count;//计数器自增。
22        notEmpty.signal();//唤醒一个消费者。
23      } finally {
24        lock.unlock();
25      }
26    }
27 
28    public Object take() throws InterruptedException {
29      lock.lock();
30      try {
31        while (count == 0) //如果计数器为零,说明没有商品,消费者需要等待。
32          notEmpty.await();
33        Object x = items[takeptr]; //从数组中通过消费者角标获取商品。
34 
35        if (++takeptr == items.length) //如果消费者角标等于数组长度,将角标归零。
36            takeptr = 0;
37        --count;//计数器自增。
38        notFull.signal();//唤醒生产者。
39        return x;
40      } finally {
41        lock.unlock();
42      }
43    } 
44  }
原文地址:https://www.cnblogs.com/sun-/p/5401215.html