java第18天----生產者消費者,Lock鎖,守護執行緒,join()方法
阿新 • • 發佈:2018-12-13
昨天知識總結:
- 多執行緒
- 執行緒的兩種建立方式
- 建立Thread的子類
- 執行緒與任務分離
- 執行緒安全----重點
- 單例中執行緒安全的應用
- 執行緒的停止
- 執行緒的兩種建立方式
執行緒
執行緒的通訊
-
執行緒的通訊:
-
分析:
-
需要兩個執行緒—輸入執行緒,和輸出執行緒,
-
需要兩個任務===輸入任務和輸出任務
-
需要一份資料
-
使用喚醒等待機制實現:wait()/notify()/notifyAll()等
-
實現:印表機列印–不斷地輸入不斷地輸出
-
分析:需要輸入任務和輸出任務共用一把鎖,保證兩個任務之間是同步的
-
給兩個任務加一把鎖,可以是this或Object.class
-
不建議使用Object.class原因:由於Object.class的範圍太大了,會造成不必要的錯誤
-
使用this最合適,因為他只被兩個執行緒共享
-
注意:只給一個執行緒加鎖,無法實現兩個執行緒的同步;
public static void main(String[] args) {
//1.建立資料類物件
Des des = new Des();
//2.建立任務類物件
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 Input implements Runnable{
Des des;
public Input() {
super();
// TODO Auto-generated constructor stub
}
public Input(Des des) {
super();
this.des = des;
}
@Override
public void run() {
// TODO Auto-generated method stub
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) {
super();
this.des = des;
}
public Output() {
super();
// TODO Auto-generated constructor stub
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
synchronized (des) {
System.out.println(des);
}
}
}
}
//建立資料類
class Des{
String name;
String sex;
@Override
public String toString() {
return "Des [名字=" + name + ", 性別=" + sex + "]";
}
}
- wait():讓當前的執行緒進入等待狀態,他會被放入一個池子(程式池),失去了搶CPU的能力,等待喚醒,(鎖—相當於給當前的執行緒做了一個標記)
- notify():讓當前的執行緒從等待執行緒喚醒,相當於從池子中取出執行緒(喚醒的是同一把鎖下的任意一個執行緒)
- notifyAll():喚醒的是同一把鎖下所有執行緒。
public static void main(String[] args) {
//1.建立資料類物件
Des1 des = new Des1();
//2.建立任務類物件
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 Input1 implements Runnable{
Des1 des;
public Input1() {
super();
// TODO Auto-generated constructor stub
}
public Input1(Des1 des) {
super();
this.des = des;
}
@Override
public void run() {
// TODO Auto-generated method stub
int i = 0;
while(true) {
if(i == 0) {
des.setData("鳳姐", "男");
}else {
des.setData("芙蓉姐姐", "女");
}
i = (i+1)%2;
}
}
}
//建立輸出任務
class Output1 implements Runnable{
Des1 des;
public Output1(Des1 des) {
super();
this.des = des;
}
public Output1() {
super();
// TODO Auto-generated constructor stub
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
des.get();
}
}
}
//建立資料類
//封裝類的原則:是你的活你做,不是你的活不要做
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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
// if(i == 1) {
// des.name = "鳳姐";
// des.sex = "男";
// }else {
// des.name = "芙蓉姐姐";
// des.sex = "女";
// }
// i = (i+1)%2;
flag = !flag;
notify();//喚醒輸出執行緒
//當執行喚醒的時候,線上程池中沒有找到被當前的所標記的執行緒,我們稱為空喚醒,空喚醒對程式沒有影響,程式允許空喚醒
}
//將資料列印到控制檯-處理資料
public synchronized void get() {
if(flag == false) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Des1 [名字=" + name + ", 性別=" + sex + "]");
flag = !flag;
notify();
}
@Override
public String toString() {
return "Des1 [名字=" + name + ", 性別=" + sex + "]";
}
}
生產者消費者
- 多執行緒的設計模式:生產著消費者
- 兩類:
- 1.單生產者單消費者
- 2.多生產者多消費者
單生產者單消費者
- 分析:生產執行緒,消費執行緒
- 生產任務,消費任務
- 產品
public static void main(String[] args) {
//1.建立產品
Product product= new Product();
//2.建立生產任務,消費任務
Producer producer = new Producer(product);
Consumer consumer = new Consumer(product);
//建立生產執行緒,消費執行緒
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) {
// TODO Auto-generated catch block
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 == true) {
try {
wait();//讓消費執行緒等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ " 生產了"+name+" 產品的數量:"+count+" 產品的價格"+price);
}
flag = !flag;
notify();//喚醒生產執行緒
}
}
//建立生產任務
class Producer implements Runnable {
Product product;
public Producer() {
super();
// TODO Auto-generated constructor stub
}
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() {
super();
// TODO Auto-generated constructor stub
}
public Consumer(Product product) {
super();
this.product = product;
}
public void run() {
while(true) {
product.consume();
}
}
}
多生產者多消費者
- 分析:生產執行緒,消費執行緒有多個
- 生產任務,消費任務各有一個
- 產品一個
public static void main(String[] args) {
//1.建立產品
Product1 product = new Product1();
//建立生產任務,消費任務
Producer1 producer1 = new Producer1(product);
Consumer1 consumer1 = new Consumer1(product);
//3.建立生產執行緒,消費執行緒
Thread t0 = new Thread(producer1);
Thread t1 = new Thread(producer1);
Thread t2 = new Thread(consumer1);
Thread t3 = new Thread(consumer1);
//開啟執行緒
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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.price = price;
count += 1;
System.out.println(Thread.currentThread().getName()+ " 生產了"+name+" 產品的數量:"+count+" 產品的價格"+price);
flag = !flag;
notifyAll();
}
//為消費者準備資料
public synchronized void consume() {
while(flag == false) {
try {
wait();//讓消費執行緒等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+ " 消費了"+name+" 產品的數量:"+count+" 產品的價格"+price);
flag = !flag;
notifyAll();
}
}
//建立生產任務
class Producer1 implements Runnable {
Product1 product;
public Producer1() {
super();
// TODO Auto-generated constructor stub
}
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() {
super();
// TODO Auto-generated constructor stub
}
public Consumer1(Product1 product) {
super();
this.product = product;
}
public void run() {
while(true) {
product.consume();
}
}
}
Lock鎖
- 比較synchronized和Lock
- 1.synchronized:從jdk1.0開始使用—隱式同步
- synchronized(鎖物件){//獲取鎖 我們簡稱這裡的鎖成為鎖旗艦或者監聽器
-
同步程式碼塊
- }//釋放鎖
- 2.Lock:從jdk1.5開始使用—顯示同步
- 原理:Lock是介面,我們要通過他的子類工作
- 首先呼叫lock的lock()方法,獲取鎖
-
進行同步操作的程式碼
- 呼叫lock的unlock()方法,釋放鎖
- 使用場景總結:當使用多生產者多消費者的時候,使用lock,其他的使用synchronized
- 使用效率上lock比synchronized高。
public static void main(String[] args) {
//1.建立產品
Product1 product = new Product1();
//建立生產任務,消費任務
Producer1 producer1 = new Producer1(product);
Consumer1 consumer1 = new Consumer1(product);
//3.建立生產執行緒,消費執行緒
Thread t0 = new Thread(producer1);
Thread t1 = new Thread(producer1);
Thread t2 = new Thread(consumer1);
Thread t3 = new Thread(consumer1);
//開啟執行緒
t0.start();
t1.start();
t2.start();
t3.start();
}
}
//建立產品類
class Product2{
String name;
double price;
int count;
//建立Lock的物件
Lock lock = new ReentrantLock();
boolean flag = false;//用來喚醒與等待之間切換
//建立用於生產者執行緒的Condition物件
Condition conditionProducer = lock.newCondition();
//建立用於消費者執行緒的Condition物件
Condition conditionConsumer = lock.newCondition();
//為生產者準備資料
public void setProduce(String name,double price) {
try {
lock.unlock();
while(flag == true) {
try {
conditionProducer.await();
} catch (InterruptedException e) {