java中线程同步问题

  先不多说,直接上个例子,著名的生产者消费者问题。

 1 public class ProducerConsumer {
 2     public static void main(String[] args) {
 3         SyncStack ss = new SyncStack();
 4         Producer p = new Producer(ss);
 5         Consumer c = new Consumer(ss);
 6         new Thread(p).start();//生产者线程
 7         new Thread(c).start();//消费者线程
 8     }
 9 }
10 /**
11 消费的物品---窝头
12 **/
13 class WoTou {
14     int id; 
15     WoTou(int id) {
16         this.id = id;
17     }
18     public String toString() {
19         return "WoTou : " + id;
20     }
21 }
22 /**
23 消费的空间---堆栈
24 **/
25 class SyncStack {
26     int index = 0;
27     WoTou[] arrWT = new WoTou[6];
28     
29     public synchronized void push(WoTou wt) {
30         while(index == arrWT.length) {//是while而不是if的原因,当wait被打断的时候依旧要检查下堆栈是否满了,否则,由于之前判断过index,从而会跳过判断而导致错误
31             try {
32                 this.wait();
33             } catch (InterruptedException e) {
34                 e.printStackTrace();
35             }
36         }
37         this.notifyAll();        
38         arrWT[index] = wt;
39         index ++;
40     }
41     
42     public synchronized WoTou pop() {
43         while(index == 0) {
44             try {
45                 this.wait();
46             } catch (InterruptedException e) {
47                 e.printStackTrace();
48             }
49         }
50         this.notifyAll();
51         index--;
52         return arrWT[index];
53     }
54 }
55 
56 /**
57 生产者类
58 **/
59 class Producer implements Runnable {
60     SyncStack ss = null;
61     Producer(SyncStack ss) {
62         this.ss = ss;
63     }
64     
65     public void run() {
66         for(int i=0; i<20; i++) {
67             WoTou wt = new WoTou(i);
68             ss.push(wt);
69 System.out.println("生产了:" + wt);
70             try {
71                 Thread.sleep((int)(Math.random() * 200));
72             } catch (InterruptedException e) {
73                 e.printStackTrace();
74             }            
75         }
76     }
77 }
78 /**
79 消费者类
80 **/
81 class Consumer implements Runnable {
82     SyncStack ss = null;
83     Consumer(SyncStack ss) {
84         this.ss = ss;
85     }
86     
87     public void run() {
88         for(int i=0; i<20; i++) {
89             WoTou wt = ss.pop();
90 System.out.println("消费了: " + wt);
91             try {
92                 Thread.sleep((int)(Math.random() * 1000));
93             } catch (InterruptedException e) {
94                 e.printStackTrace();
95             }            
96         }
97     }
98 }

 效果如图:

synchronized的引入,学过数据库系统和操作系统的朋友应该会有所了解,对于共享资源,当一个线程占用的时候不能被其他线程操作,和操作系统的原子操作的原理有些相似。

对于synchronized的理解:

synchronized(对象){

同步代码块

}

当是非static synchronized函数时,默认对象为this;当为是static synchronized函数时,默认为当前对象.class。

而wait,notify,notifyAll时,前面必须指定锁,即对象(原因:因为如果出现synchronized嵌套的时候会出错,这个好比一个游戏,一个锁是一组,只有在一组之内wait,notify,notifyAll有效,而对于另外一个组是无效的。)

  wait的引入:例如上题堆栈中只能放6个窝头,但是当堆栈中有6个窝头的时候,生产者获得对象锁的时候就不能生产了,因此,生产者就会进入wait池,同时又会释放对象锁。

  notify的引入:唤醒wait池中的一个线程(ps:只能唤醒别的线程,不能唤醒自己)

  notifyall的引入:唤醒wait池中的所有线程

  在最后,给大家说下wait和sleep的区别吧,个人理解:

1.wait是Object类中方法,会抛出InteruptException,sleep是Thread类中的方法,wait方法只用在获得对象锁得时候才能用,否则会抛异常;

2.wait方法会释放对象锁,但是sleep不会释放对象锁;

3.wait后的线程需要其他的线程去唤醒,但是sleep的线程在规定的时间后会自动唤醒。

原文地址:https://www.cnblogs.com/liujunming/p/4375864.html