1.首先我們回顧一下面試題:
兩個線程,一個線程打印1-52,另一個打印字母A-Z打印順序為12A34B...5152Z, 要求用線程間通信
這個面試題就就是完全考察線程之間的通信機制,常用的技術分為二種
- 一種是Object 類中的自帶的 wait 和 notify 機制,
- 二是 lock中的通信機制 Condition接口中的 await 和 signal 方法。
線程間通信:1、生產者+消費者2、通知等待喚醒機制
- Object 類中的自帶的 wait 和 notify 機制實現, synchronized用來辦證線程安全
package com.atguigu.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class ShareDataOne//資源類 { private int number = 0;//初始值為零的一個變量 public synchronized void increment() throws InterruptedException { //1判斷 if(number !=0 ) { this.wait(); } //2干活 ++number; System.out.println(Thread.currentThread().getName()+"\t"+number); //3通知 this.notifyAll(); } public synchronized void decrement() throws InterruptedException { // 1判斷 if (number == 0) { this.wait(); } // 2干活 --number; System.out.println(Thread.currentThread().getName() + "\t" + number); // 3通知 this.notifyAll(); } } /** * * @Description: *現在兩個線程, * 可以操作初始值為零的一個變量, * 實現一個線程對該變量加1,一個線程對該變量減1, * 交替,來10輪。 * @author xialei * * * 筆記:Java里面如何進行工程級別的多線程編寫 * 1 多線程變成模板(套路)-----上 * 1.1 線程 操作 資源類 * 1.2 高內聚 低耦合 * 2 多線程變成模板(套路)-----下 * 2.1 判斷 * 2.2 干活 * 2.3 通知 */ public class NotifyWaitDemoOne { public static void main(String[] args) { ShareDataOne sd = new ShareDataOne(); new Thread(() -> { for (int i = 1; i < 10; i++) { try { sd.increment(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 1; i < 10; i++) { try { sd.decrement(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }, "B").start(); } }
注意點:線程通信最好不要用 if 去判斷,用 while 去判斷,因為用 if 去判斷容易造成虛假喚醒,以下是修改之后的數據
package com.atguigu.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.omg.IOP.Codec; class ShareData//資源類 { private int number = 0;//初始值為零的一個變量 public synchronized void increment() throws InterruptedException { //判斷 while(number!=0) { this.wait(); } //干活 ++number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 this.notifyAll();; } public synchronized void decrement() throws InterruptedException { //判斷 while(number!=1) { this.wait(); } //干活 --number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 this.notifyAll(); } } /** * * @Description: *現在兩個線程, * 可以操作初始值為零的一個變量, * 實現一個線程對該變量加1,一個線程對該變量減1, * 交替,來10輪。 * @author xialei * * * 筆記:Java里面如何進行工程級別的多線程編寫 * 1 多線程變成模板(套路)-----上 * 1.1 線程 操作 資源類 * 1.2 高內聚 低耦合 * 2 多線程變成模板(套路)-----下 * 2.1 判斷 * 2.2 干活 * 2.3 通知 */ public class NotifyWaitDemo { public static void main(String[] args) { ShareData sd = new ShareData(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { sd.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { sd.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { sd.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { sd.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "D").start(); } } /* * * * 2 多線程變成模板(套路)-----下 * 2.1 判斷 * 2.2 干活 * 2.3 通知 * 3 防止虛假喚醒用while * * * */
使用 Condition去做
package com.atguigu.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.omg.IOP.Codec; class ShareData//資源類 { private int number = 0;//初始值為零的一個變量 private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void increment() throws InterruptedException { lock.lock(); try { //判斷 while(number!=0) { condition.await(); } //干活 ++number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void decrement() throws InterruptedException { lock.lock(); try { //判斷 while(number!=1) { condition.await(); } //干活 --number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } /*public synchronized void increment() throws InterruptedException { //判斷 while(number!=0) { this.wait(); } //干活 ++number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 this.notifyAll();; } public synchronized void decrement() throws InterruptedException { //判斷 while(number!=1) { this.wait(); } //干活 --number; System.out.println(Thread.currentThread().getName()+" \t "+number); //通知 this.notifyAll(); }*/ } /** * * @Description: *現在兩個線程, * 可以操作初始值為零的一個變量, * 實現一個線程對該變量加1,一個線程對該變量減1, * 交替,來10輪。 * * * 筆記:Java里面如何進行工程級別的多線程編寫 * 1 多線程變成模板(套路)-----上 * 1.1 線程 操作 資源類 * 1.2 高內聚 低耦合 * 2 多線程變成模板(套路)-----下 * 2.1 判斷 * 2.2 干活 * 2.3 通知 */ public class NotifyWaitDemo { public static void main(String[] args) { ShareData sd = new ShareData(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { sd.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { sd.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { sd.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { sd.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "D").start(); } } /* * * * 2 多線程變成模板(套路)-----下 * 2.1 判斷 * 2.2 干活 * 2.3 通知 * 3 防止虛假喚醒用while * * * */