Java多线程(六)线程通信
线程通信的例子
使用两个线程打印 1-100。线程1, 线程2 交替打印
涉及到的三个方法:
- wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
- notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
- notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
说明:
- wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
- wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常
- wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
面试题:sleep() 和 wait()的异同?
1. 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
2. 不同点:1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
2. 调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
3. 关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。(这里锁即同步监视器)
1 class Number implements Runnable{ 2 private int number = 1; 3 private Object obj = new Object(); 4 @Override 5 public void run() { 6 7 while(true){ 8 //当线程1进入同步监视器,进行输出后,通过wait方法被阻塞并释放同步监视器。此时线程2进入 9 //线程2进入同步监视器,首先通过notify方法使线程1由阻塞变为就绪,但此时同步监视器由线程2操作,所以线程1保持就绪直至线程2释放资源 10 synchronized (obj) { 11 12 obj.notify(); 13 14 if(number <= 100){ 15 16 try { 17 Thread.sleep(10); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 22 System.out.println(Thread.currentThread().getName() + ":" + number); 23 number++; 24 25 try { 26 //使得调用如下wait()方法的线程进入阻塞状态 27 obj.wait(); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 32 }else{ 33 break; 34 } 35 } 36 37 } 38 39 } 40 }
生产者/消费者问题
经典的线程同步问题,这里需要线程通信的方法来解决该问题。
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
分析:
1. 是否是多线程问题?是,生产者线程,消费者线程
2. 是否有共享数据?是,店员(或产品)
3. 如何解决线程的安全问题?同步机制,有三种方法
4. 是否涉及线程的通信?是
1 class Clerk{ 2 3 private int productCount = 0; 4 //生产产品 5 public synchronized void produceProduct() { 6 7 if(productCount < 20){ 8 productCount++; 9 System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "个产品"); 10 11 notify(); 12 13 }else{ 14 //等待 15 try { 16 wait(); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 } 21 } 22 //消费产品 23 public synchronized void consumeProduct() { 24 if(productCount > 0){ 25 System.out.println(Thread.currentThread().getName() + ":开始消费第" + productCount + "个产品"); 26 productCount--; 27 28 notify(); 29 }else{ 30 //等待 31 try { 32 wait(); 33 } catch (InterruptedException e) { 34 e.printStackTrace(); 35 } 36 } 37 38 } 39 } 40 41 class Producer extends Thread{//生产者 42 43 private Clerk clerk; 44 45 public Producer(Clerk clerk) { 46 this.clerk = clerk; 47 } 48 49 @Override 50 public void run() { 51 System.out.println(getName() + ":开始生产产品....."); 52 53 while(true){ 54 55 try { 56 Thread.sleep(10); 57 } catch (InterruptedException e) { 58 e.printStackTrace(); 59 } 60 61 clerk.produceProduct(); 62 } 63 64 } 65 } 66 67 class Consumer extends Thread{//消费者 68 private Clerk clerk; 69 70 public Consumer(Clerk clerk) { 71 this.clerk = clerk; 72 } 73 74 @Override 75 public void run() { 76 System.out.println(getName() + ":开始消费产品....."); 77 78 while(true){ 79 80 try { 81 Thread.sleep(20); 82 } catch (InterruptedException e) { 83 e.printStackTrace(); 84 } 85 86 clerk.consumeProduct(); 87 } 88 } 89 } 90 91 public class ProductTest { 92 93 public static void main(String[] args) { 94 Clerk clerk = new Clerk(); 95 96 Producer p1 = new Producer(clerk); 97 p1.setName("生产者1"); 98 99 Consumer c1 = new Consumer(clerk); 100 c1.setName("消费者1"); 101 Consumer c2 = new Consumer(clerk); 102 c2.setName("消费者2"); 103 104 p1.start(); 105 c1.start(); 106 c2.start(); 107 108 } 109 }

浙公网安备 33010602011771号