由生産者消費者模型引出的線程同步問題
基本生産者消費者模型:
代碼示例:
資料模型:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:00
*/
public class Message {
private String tittle;
private String content;
public String getTittle() {
return tittle;
}
public void setTittle(String tittle) {
this.tittle = tittle;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
生産者模型:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:00
*/
public class Producer implements Runnable{
private Message msg;
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
this.msg.setTittle("1.");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.msg.setContent("第一種資料模型");
}else {
this.msg.setContent("2.");
this.msg.setContent("第二種資料模型");
}
}
}
}
消費者模型:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:00
*/
public class Consumer implements Runnable{
private Message msg;
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.msg.getTittle()+" "+this.msg.getContent());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
主程式:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:00
*/
public class Main {
public static void main(String[] args) {
Message msg = new Message();
//啟動生産者模型
new Thread(new Producer(msg)).start();
//啟動消費者模型
new Thread(new Consumer(msg)).start();
}
}
執行結果:
控制台列印結果令人大跌眼鏡,說好的生産一個模型取走一個模型呢?連生産出來的模型都亂了,1号模型對應這2号模型的内容,居然還有null。此時我發現了兩個主要問題:
問題一: 資料不同步;
問題二:重複生産和重複取出問題;
問題解決
如果要解決問題,首要解決的就是資料同步的處理問題,如果想要解決資料同步最簡單的方法就是使用synchronized關鍵字定義同步代碼塊或同步方法,于是這個時候對于同步問題的處理就可以直接在Message類中完成。
解決同步問題:
在進行同步處理的時候肯定需要有一個同步處理的對象,那麼此時肯定要将同步操作交由Message來處理。在資料模型Message中修改生産和消費方法,并加上synchronized關鍵字同步方法。
解決同步問題後的代碼示例:
資料模型:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:10
*/
public class Message {
private String tittle;
private String content;
public synchronized void set(String tittle, String content) {
this.tittle = tittle;
try {
//模拟生産過程 10毫秒
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content = content;
}
public synchronized String get() {
try {
//模拟消費過程 10毫秒
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return this.tittle + " - " + this.content;
}
}
生産者模型:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:10
*/
public class Producer implements Runnable {
private Message msg;
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
this.msg.set("1.", "第一種資料模型");
} else {
this.msg.set("2.", "第二種資料模型");
}
}
}
}
消費者模型:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:10
*/
public class Consumer implements Runnable {
private Message msg;
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.msg.get());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
主程式:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:10
*/
public class Main {
public static void main(String[] args) {
Message msg = new Message();
//啟動生産者模型
new Thread(new Producer(msg)).start();
//啟動消費者模型
new Thread(new Consumer(msg)).start();
}
}
執行結果:
根據控制台列印輸出可以觀察到,剛剛發現的同步問題已經解決了,生産和消費的模型資訊都是正确的 1号模型對應第一種資料模型,2号對應第二種資料模型,完全正确,但重複生産重複消費問題依然存在。
接下來我來解決這個重複生産重複消費的問題,典型思路就是,設定一個标記,好比十字路口的紅綠燈,綠燈亮時生産者進行生産,消費者等待,紅燈亮時消費者進行消費,而生産者停止生産等待消費者消費完成信号燈變為綠色時再進行生産。
終極版本代碼示例:
資料模型:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:14
*/
public class Message {
private String tittle;
private String content;
/** flag屬性控制生産與消費
* true:允許生産,不允許消費 false:允許消費,不允許生産
* 預設可以生産
*/
private boolean flag = true;
public synchronized void set(String tittle,String content){
if (!this.flag){
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.tittle=tittle;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content=content;
//标記為已生産
this.flag=false;
//喚醒等待線程
super.notify();
}
public synchronized String get() {
if (this.flag){
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
return this.tittle+" - "+this.content;
}finally {
//繼續生産
this.flag=true;
//喚醒等待線程
super.notify();
}
}
}
生産者模型:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:14
*/
public class Producer implements Runnable{
private Message msg;
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
this.msg.set("1.","第一種資料模型");
}else {
this.msg.set("2.","第二種資料模型");
}
}
}
}
消費者模型:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:14
*/
public class Consumer implements Runnable{
private Message msg;
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.msg.get());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
主程式:
/**
* Created by IntelliJ IDEA.
*
* @Author: ZhangDong
* @Date: 2019/9/9 16:14
*/
public class Main {
public static void main(String[] args) {
Message msg = new Message();
//啟動生産者模型
new Thread(new Producer(msg)).start();
//啟動消費者模型
new Thread(new Consumer(msg)).start();
}
}
執行結果:
至此,由生産者消費者模型引出的線程同步問題已經解決,希望能給看到此文章的你一些思路以及解決方法。