java多執行緒:執行緒的通訊、喚醒等待機制、生產消費者模式、Lock
阿新 • • 發佈:2018-12-10
執行緒的通訊:印表機列印–不斷的輸入輸出
package com.qianfeng.test; /* * 執行緒的通訊: * 分析: * 需要兩個執行緒--輸入執行緒和輸出執行緒 * 需要兩個任務--輸入任務和輸出任務 * 需要一份資料 * * 實現:印表機列印--不斷的輸入不斷的輸出 * */ public class Demo2 { public static void main(String[] args) { //1.建立資料類物件 Des des =new Des(); //建立任務類物件並繫結資料 Input input = new Input(des); Output output = new Output(des); //3.建立執行緒 Thread inputThread = new Thread(input); Thread outputThread = new Thread(output); //4.開啟執行緒 inputThread.start(); outputThread.start(); } } //建立資料類 class Des{ String name; String sex; } //建立輸入任務 class Input implements Runnable{ Des des; public Input(Des des){ this.des=des; } public void run() { /* * 分析:需要輸入任務與輸出任務共用一把鎖,保證兩個任務之間是同步的。 * 給兩個任務加一把鎖,鎖可以是des或Object.class * * 不建議使用Object.class,因為Object.class應用範圍太大,其它地方可能也會用到。 * 使用des最合適,因為它只被兩個執行緒共享。 * * 注意:只給一個任務加鎖無法實現兩個執行緒的同步。 */ int i = 0; while(true){ synchronized (des) { if (i==1) { des.name = "鳳姐"; des.sex = "男" ; }else { des.name = "芙蓉姐姐"; des.sex = "女" ; } i=(i+1)%2; } } } } //建立輸出任務 class Output implements Runnable{ Des des; public Output(Des des){ this.des=des; } public void run() { while(true){ synchronized (des) { System.out.println("姓名:"+des.name+" 性別"+des.sex); } } } }
喚醒等待機制,wait()/notify()/notifyAll()
package com.qianfeng.test; /* * 執行緒的通訊: * 分析: * 需要兩個執行緒--輸入執行緒和輸出執行緒 * 需要兩個任務--輸入任務和輸出任務 * 需要一份資料 * * 實現:印表機列印--一次輸入一次輸出 * * 使用喚醒等待機制實現:wait()/notify()/notifyAll()等 * wait():讓當前的執行緒進入等待的狀態,他會被放入一個池子(執行緒池),失去了搶cpu的能力,等待喚醒。(鎖--相當於給當前執行緒做了一個標記) * notify():讓當前的執行緒從等待執行緒喚醒,相當於從池子中取出執行緒。(喚醒的是同一把鎖下的任意一個執行緒) * notifyAll():喚醒的是同一把鎖下的所有執行緒。 */ public class Demo3 { public static void main(String[] args) { // 1.建立資料類物件 Des1 des = new Des1(); // 建立任務類物件並繫結資料 Input1 input = new Input1(des); Output1 output = new Output1(des); // 3.建立執行緒 Thread inputThread = new Thread(input); Thread outputThread = new Thread(output); // 4.開啟執行緒 inputThread.start(); outputThread.start(); } } // 建立資料類 // 封裝類的原則:是你的活你做,不是你的活不要做。 class Des1 { String name; String sex; boolean flag = false;// 建立一個標識,控制喚醒與等待的切換。 // 獲取資料--處理輸入 public synchronized void setData(String name, String sex) { if (flag == true) { try {// 讓輸入執行緒等待,當flag值為true的時候 // 在執行程式碼的時候,這裡對應的是哪個執行緒,鎖物件操作的就是哪個執行緒。 wait();// 只要一執行wait,執行緒會立刻停在這裡,等待下次喚醒。 } catch (InterruptedException e) { e.printStackTrace(); } } this.name = name; this.sex = sex; flag = !flag; //// 喚醒輸出執行緒。 // 當執行喚醒的時候,線上程池中沒有找到被當前的鎖標記的執行緒,我們稱為空喚醒,空喚醒對程式沒有影響,程式允許空喚醒。 notify(); } // 將資料列印到控制檯--處理輸出 public synchronized void getData() { if (flag == false) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("姓名:" + name + " 性別" + sex); flag = !flag; notify(); } } // 建立輸入任務 class Input1 implements Runnable { Des1 des; public Input1(Des1 des) { this.des = des; } public void run() { int i = 0; while (true) { if (i == 1) { des.setData("鳳姐", "男"); } else { des.setData("芙蓉姐姐", "女"); } i = (i + 1) % 2; } } } // 建立輸出任務 class Output1 implements Runnable { Des1 des; public Output1(Des1 des) { this.des = des; } public void run() { while(true){ des.getData(); } } }
多執行緒設計模式:單生產者單消費者
package com.qianfeng.test; /* * 多執行緒的設計模式:生產者消費者 * 兩類: * 1.單生產者單消費者 * 2.多生產者多消費者 * * 先研究單生產者單消費者 * 分析: * 生產執行緒,消費執行緒 * 生產任務,消費任務 * 產品 */ public class Demo4 { public static void main(String[] args) { //1.建立產品 Product product = new Product(); //2.建立生產任務,消費任務 Producer producer = new Producer(product); Consumer consumer = new Consumer(product); //3.建立生產執行緒,消費執行緒 Thread t1 = new Thread(producer); Thread t2 = new Thread(consumer); //4.開啟執行緒 t1.start(); t2.start(); } } //建立產品類 class Product{ String name; double price; int count; boolean flag = false;//用來在喚醒與等待之間進行切換 //為生產準備資料 public synchronized void setProduce(String name,double price){ if (flag == true) { try { wait();//讓生產執行緒等待 } catch (InterruptedException e) { e.printStackTrace(); } } this.name = name; this.price = price; System.out.println(Thread.currentThread().getName()+" 生產了:"+name+" 產品的數量:"+count+" 產品的價格:"+price); count++; flag = ! flag; notify();//喚醒消費執行緒 } //為消費準備資料 public synchronized void consume() { if (flag == false) { try { wait();//讓消費執行緒等待 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+" 消費了:"+name+" 產品的數量:"+count+" 產品的價格:"+price); flag = ! flag; notify();//喚醒生產執行緒 } } //建立生產任務 class Producer implements Runnable{ Product product; public Producer(Product product) { super(); this.product = product; } public void run() { while (true) { product.setProduce("bingbing", 10); } } } //建立消費任務 class Consumer implements Runnable{ Product product; public Consumer(Product product) { super(); this.product = product; } public void run() { while (true) { product.consume(); } } }
多執行緒設計模式:多生產者多消費者
package com.qianfeng.test;
/*
* 多執行緒的設計模式:生產者消費者
* 兩類:
* 1.單生產者單消費者
* 2.多生產者多消費者
*
* 研究多生產者多消費者
* 分析:
* 生產執行緒,消費執行緒有多個
* 生產任務,消費任務各有一個
* 產品一個
*/
public class Demo5 {
public static void main(String[] args) {
//1.建立產品
Product1 product = new Product1();
//2.建立生產任務,消費任務
Producer1 producer = new Producer1(product);
Consumer1 consumer = new Consumer1(product);
//3.建立生產執行緒,消費執行緒
Thread t0 = new Thread(producer);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
Thread t3 = new Thread(consumer);
//4.開啟執行緒
t0.start();
t1.start();
t2.start();
t3.start();
}
}
//建立產品類
class Product1{
String name;
double price;
int count;
boolean flag = false;//用來在喚醒與等待之間進行切換
//為生產準備資料
public synchronized void setProduce(String name,double price){
while (flag == true) {
try {
wait();//讓生產執行緒等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.price = price;
System.out.println(Thread.currentThread().getName()+" 生產了:"+name+" 產品的數量:"+count+" 產品的價格:"+price);
count++;
flag = ! flag;
//notify();//喚醒消費執行緒
notifyAll();
}
//為消費準備資料
public synchronized void consume() {
while (flag == false) {
try {
wait();//讓消費執行緒等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 消費了:"+name+" 產品的數量:"+count+" 產品的價格:"+price);
flag = ! flag;
//notify();//喚醒生產執行緒
notifyAll();
}
}
//建立生產任務
class Producer1 implements Runnable{
Product1 product;
public Producer1(Product1 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.setProduce("bingbing", 10);
}
}
}
//建立消費任務
class Consumer1 implements Runnable{
Product1 product;
public Consumer1(Product1 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.consume();
}
}
}
Lock,Condition。Lock與synchronized的比較
package com.qianfeng.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* Lock:
* 比較synchronized和Lock
* 1.synchronized:從jdk1.0開始使用----隱式同步
* synchronized(鎖物件){//獲取鎖 我們將這裡的鎖稱為鎖旗艦或者監聽器
* 同步的程式碼
* }//釋放鎖
* 2.Lock:從jdk1.5開始使用----顯示同步
* 原理:Lock是介面,我們要通過他的子類工作
* 具體的工作流程:
* 首先呼叫lock的lock()方法,獲取鎖
* 進行同步操作的程式碼
* 呼叫lock的unlock()方法,釋放鎖
*
* 使用場景總結:
* 1.當進行多生產者的消費者的時候,使用lock,其他的使用synchronized
*
* 使用效率上lock比synchronized高.
*/
public class Demo6 {
public static void main(String[] args) {
//1.建立產品
Product2 product = new Product2();
//2.建立生產任務,消費任務
Producer2 producer = new Producer2(product);
Consumer2 consumer = new Consumer2(product);
//3.建立生產執行緒,消費執行緒
Thread t0 = new Thread(producer);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
Thread t3 = new Thread(consumer);
//4.開啟執行緒
t0.start();
t1.start();
t2.start();
t3.start();
}
}
//建立產品類
class Product2{
String name;
double price;
int count;
boolean flag = false;//用來在喚醒與等待之間進行切換
//建立Lock的物件
Lock lock = new ReentrantLock();
//建立用於生產執行緒的Condition物件
Condition proCon = lock.newCondition();
//建立用於消費執行緒的Condition物件
Condition conCon = lock.newCondition();
//為生產準備資料
public void setProduce(String name,double price){
try {
lock.lock();//獲取鎖
while (flag == true) {
try {
//wait();//讓生產執行緒等待
proCon.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.price = price;
System.out.println(Thread.currentThread().getName()+" 生產了:"+name+" 產品的數量:"+count+" 產品的價格:"+price);
count++;
flag = ! flag;
//notify();//喚醒消費執行緒
//notifyAll();
conCon.signal();
} finally {//必須執行的程式碼
lock.unlock();//釋放鎖
}
}
//為消費準備資料
public void consume() {
try {
lock.lock();
while (flag == false) {
try {
//wait();//讓消費執行緒等待
conCon.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 消費了:"+name+" 產品的數量:"+count+" 產品的價格:"+price);
flag = ! flag;
//notify();//喚醒生產執行緒
//notifyAll();
proCon.signal();
} finally {
lock.unlock();
}
}
}
//建立生產任務
class Producer2 implements Runnable{
Product2 product;
public Producer2(Product2 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.setProduce("bingbing", 10);
}
}
}
//建立消費任務
class Consumer2 implements Runnable{
Product2 product;
public Consumer2(Product2 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.consume();
}
}
}
守護執行緒,setDaemon。
package com.qianfeng.test;
/*
* 守護執行緒:相當於後臺執行緒,依賴於前臺執行緒。正常情況下,當前臺執行緒結束的時候,不管後臺執行緒有沒有結束,都會立即結束。
* 典型的守護執行緒:垃圾回收執行緒。
*
*/
public class Demo7 {
public static void main(String[] args) {
Test test = new Test();
Thread thread = new Thread(test);
/*
* 當程式呼叫setDaemon的時候,並且將引數設定為true,他就變成了守護執行緒。
* 注意:這個方法一定要在start方法之前呼叫。
*/
thread.setDaemon(true);
thread.start();
int i = 0;
while(true){
if (i++ == 10 ) {
System.out.println(Thread.currentThread().getName()+" i"+i);
break;//主執行緒結束
}
}
}
}
class Test implements Runnable{
public void run() {
while(true){
System.out.println("守護執行緒");
}
}
}
join方法
package com.qianfeng.test;
/*
* join()方法:
* 原理:程式一旦呼叫了join方法,他的優先順序會高於主執行緒。意思是說,主執行緒會等當前執行緒執行完後再去執行。
* 注意:這個執行緒的優先順序只比main高,對其它的執行緒沒有影響。
*
*/
public class Demo8 {
public static void main(String[] args) {
Dog dog = new Dog();
Thread thread0 = new Thread(dog);
Thread thread1 = new Thread(dog);
thread0.start();
thread1.start();
/*
* 當執行緒開始工作後,讓thread0呼叫join()方法,它的優先順序會高於main執行緒。
* 注意:join()方法必須線上程開始工作後執行
*/
try {
thread0.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" i"+i);
}
}
}
class Dog implements Runnable{
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" i"+i);
}
}
}