今天在草稿里翻到一篇古董级学习心得,分享出来给大家。
学习多线程同步的生产者和消费者问题,目标是两个线程共同访问一个仓库,一个生产,一个消费,仓库大小为10,总共要生产50个产品,代码如下:
com.example.producercomsumer;
import java.util.LinkedList;
import java.util.Random;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends Activity {
public static final String TAG = "";
public static final int FULL_SIZE = 10;
private LinkedList mStorage = new LinkedList();
private static final int TOTAL_NUM = 50;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Producer().start();
new Comsumer().start();
}
class Producer extends Thread {
int total = 0;
public void run() {
Log.d(TAG, "Start Produce>>>>>>>>>>>>>>>");
while (total < TOTAL_NUM) {
synchronized (mStorage) {
while (mStorage.size() == FULL_SIZE) {
try {
mStorage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String product = "PRODUCT(" + new Random().nextInt() + ")";
total++;
mStorage.add(product);
Log.d(TAG, "Produce:" + product + " storage size:" + mStorage.size() + " , total:" + total);
mStorage.notify();
Thread.yield();
}
}
// Log.d(TAG, "End Produce<<<<<<<<<<<<<<<");
}
}
class Comsumer extends Thread {
public void run() {
while (true) {
synchronized (mStorage) {
while (mStorage.size() == 0) {
try {
mStorage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String product = mStorage.removeFirst();
Log.d(TAG, "Consume : " + product + " storage size:" + mStorage.size());
mStorage.notify();
Thread.yield();
}
}
}
}
}
运行结果:
D/ ( 8368): Start Produce>>>>>>>>>>>>>>>
D/ ( 8368): Produce:PRODUCT(638723307) storage size:1 , total:1
D/ ( 8368): Produce:PRODUCT(1004619511) storage size:2 , total:2
D/ ( 8368): Produce:PRODUCT(-498194369) storage size:3 , total:3
D/ ( 8368): Produce:PRODUCT(723383389) storage size:4 , total:4
D/ ( 8368): Produce:PRODUCT(567560084) storage size:5 , total:5
D/ ( 8368): Produce:PRODUCT(1379010839) storage size:6 , total:6
D/ ( 8368): Produce:PRODUCT(1154317481) storage size:7 , total:7
D/ ( 8368): Produce:PRODUCT(941936088) storage size:8 , total:8
D/ ( 8368): Produce:PRODUCT(2022311000) storage size:9 , total:9
D/ ( 8368): Produce:PRODUCT(1809929607) storage size:10 , total:10
I/ZDClock ( 5221): ScheduleReceiver finish…
D/ ( 8368): Consume : PRODUCT(638723307) storage size:9
D/ ( 8368): Produce:PRODUCT(-1766711493) storage size:10 , total:11
D/ ( 8368): Consume : PRODUCT(1004619511) storage size:9
D/ ( 8368): Produce:PRODUCT(-549750722) storage size:10 , total:12
D/ ( 8368): Consume : PRODUCT(-498194369) storage size:9
D/ ( 8368): Produce:PRODUCT(-1021068124) storage size:10 , total:13
D/ ( 8368): Consume : PRODUCT(723383389) storage size:9
D/ ( 8368): Produce:PRODUCT(-1835566222) storage size:10 , total:14
D/ ( 8368): Consume : PRODUCT(567560084) storage size:9
D/ ( 8368): Produce:PRODUCT(2075806421) storage size:10 , total:15
D/ ( 8368): Consume : PRODUCT(1379010839) storage size:9
D/ ( 8368): Produce:PRODUCT(-1123780757) storage size:10 , total:16
D/ ( 8368): Consume : PRODUCT(1154317481) storage size:9
D/ ( 8368): Produce:PRODUCT(-1505451665) storage size:10 , total:17
D/ ( 8368): Consume : PRODUCT(941936088) storage size:9
D/ ( 8368): Produce:PRODUCT(-310421582) storage size:10 , total:18
D/ ( 8368): Consume : PRODUCT(2022311000) storage size:9
D/ ( 8368): Produce:PRODUCT(-952567496) storage size:10 , total:19
D/ ( 8368): Consume : PRODUCT(1809929607) storage size:9
D/ ( 8368): Produce:PRODUCT(207450438) storage size:10 , total:20
D/ ( 8368): Consume : PRODUCT(-1766711493) storage size:9
D/ ( 8368): Produce:PRODUCT(-174220471) storage size:10 , total:21
D/ ( 8368): Consume : PRODUCT(-549750722) storage size:9
D/ ( 8368): Produce:PRODUCT(-987564322) storage size:10 , total:22
D/ ( 8368): Consume : PRODUCT(-1021068124) storage size:9
D/ ( 8368): Produce:PRODUCT(-1369619979) storage size:10 , total:23
D/ ( 8368): Consume : PRODUCT(-1835566222) storage size:9
D/ ( 8368): Produce:PRODUCT(120127762) storage size:10 , total:24
D/ ( 8368): Consume : PRODUCT(2075806421) storage size:9
D/ ( 8368): Produce:PRODUCT(-263466892) storage size:10 , total:25
D/ ( 8368): Consume : PRODUCT(-1123780757) storage size:9
D/ ( 8368): Produce:PRODUCT(853459165) storage size:10 , total:26
D/ ( 8368): Consume : PRODUCT(-1505451665) storage size:9
D/ ( 8368): Produce:PRODUCT(471788256) storage size:10 , total:27
D/ ( 8368): Consume : PRODUCT(-310421582) storage size:9
D/ ( 8368): Produce:PRODUCT(1667203089) storage size:10 , total:28
D/ ( 8368): Consume : PRODUCT(-952567496) storage size:9
D/ ( 8368): Produce:PRODUCT(1285532180) storage size:10 , total:29
D/ ( 8368): Consume : PRODUCT(207450438) storage size:9
D/ ( 8368): Produce:PRODUCT(-737646103) storage size:10 , total:30
D/ ( 8368): Consume : PRODUCT(-174220471) storage size:9
D/ ( 8368): Produce:PRODUCT(-1108928791) storage size:10 , total:31
D/ ( 8368): Consume : PRODUCT(-987564322) storage size:9
D/ ( 8368): Produce:PRODUCT(8382015) storage size:10 , total:32
D/ ( 8368): Consume : PRODUCT(-1369619979) storage size:9
D/ ( 8368): Produce:PRODUCT(-373673643) storage size:10 , total:33
D/ ( 8368): Consume : PRODUCT(120127762) storage size:9
D/ ( 8368): Produce:PRODUCT(885224758) storage size:10 , total:34
D/ ( 8368): Consume : PRODUCT(-263466892) storage size:9
D/ ( 8368): Produce:PRODUCT(480084166) storage size:10 , total:35
D/ ( 8368): Consume : PRODUCT(853459165) storage size:9
D/ ( 8368): Produce:PRODUCT(1689349959) storage size:10 , total:36
D/ ( 8368): Consume : PRODUCT(471788256) storage size:9
D/ ( 8368): Produce:PRODUCT(277721569) storage size:10 , total:37
D/ ( 8368): Consume : PRODUCT(1667203089) storage size:9
D/ ( 8368): Produce:PRODUCT(-105873084) storage size:10 , total:38
D/ ( 8368): Consume : PRODUCT(1285532180) storage size:9
D/ ( 8368): Produce:PRODUCT(1088002753) storage size:10 , total:39
D/ ( 8368): Consume : PRODUCT(-737646103) storage size:9
D/ ( 8368): Produce:PRODUCT(607836125) storage size:10 , total:40
D/ ( 8368): Consume : PRODUCT(-1108928791) storage size:9
D/ ( 8368): Produce:PRODUCT(1803250958) storage size:10 , total:41
D/ ( 8368): Consume : PRODUCT(8382015) storage size:9
D/ ( 8368): Produce:PRODUCT(1369254199) storage size:10 , total:42
D/ ( 8368): Consume : PRODUCT(-373673643) storage size:9
D/ ( 8368): Produce:PRODUCT(-1414804167) storage size:10 , total:43
D/ ( 8368): Consume : PRODUCT(885224758) storage size:9
D/ ( 8368): Produce:PRODUCT(-1863036635) storage size:10 , total:44
D/ ( 8368): Consume : PRODUCT(480084166) storage size:9
D/ ( 8368): Produce:PRODUCT(1626666537) storage size:10 , total:45
D/ ( 8368): Consume : PRODUCT(1689349959) storage size:9
D/ ( 8368): Produce:PRODUCT(1244610879) storage size:10 , total:46
D/ ( 8368): Consume : PRODUCT(277721569) storage size:9
D/ ( 8368): Produce:PRODUCT(-1931891364) storage size:10 , total:47
D/ ( 8368): Consume : PRODUCT(-105873084) storage size:9
D/ ( 8368): Produce:PRODUCT(1981405023) storage size:10 , total:48
D/ ( 8368): Consume : PRODUCT(1088002753) storage size:9
D/ ( 8368): Produce:PRODUCT(-1218182155) storage size:10 , total:49
D/ ( 8368): Consume : PRODUCT(607836125) storage size:9
D/ ( 8368): Produce:PRODUCT(-1601776808) storage size:10 , total:50
D/ ( 8368): Consume : PRODUCT(1803250958) storage size:9
D/ ( 8368): Consume : PRODUCT(1369254199) storage size:8
D/ ( 8368): Consume : PRODUCT(-1414804167) storage size:7
D/ ( 8368): Consume : PRODUCT(-1863036635) storage size:6
D/ ( 8368): Consume : PRODUCT(1626666537) storage size:5
D/ ( 8368): Consume : PRODUCT(1244610879) storage size:4
D/ ( 8368): Consume : PRODUCT(-1931891364) storage size:3
D/ ( 8368): Consume : PRODUCT(1981405023) storage size:2
D/ ( 8368): Consume : PRODUCT(-1218182155) storage size:1
D/ ( 8368): Consume : PRODUCT(-1601776808) storage size:0
发现是这样wait的:
while (mStorage.size() == FULL_SIZE) {
try {
mSync.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
像这样:
class Producer extends Thread {
int total = 0;
public void run() {
Log.d(TAG, "Start Produce》》》》》》》》》》》》》》》》》》》》");
while (total < TOTAL_NUM) {
synchronized (mSync) {
if (mStorage.size() == FULL_SIZE) {
try {
mSync.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String product = "PRODUCT(" + new Random().nextInt() + ")";
total++;
mStorage.add(product);
Log.d(TAG, "Produce:" + product + " storage size:" + mStorage.size() + " , total:" + total);
mSync.notify();
Thread.yield();
}
}
Log.d(TAG, "End Produce《《《《《《《《《《《《《《《");
}
}
class Comsumer extends Thread {
public void run() {
while (true) {
synchronized (mSync) {
if (mStorage.size() == 0) {
try {
mSync.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String product = mStorage.removeFirst();
Log.d(TAG, "Consume : " + product + " storage size:" + mStorage.size());
mSync.notify();
Thread.yield();
}
}
}
}
现在尝试把事情变得好玩点,添加一个消费者,变成一个人生产,两个人消费:
class Comsumer extends Thread {
private String mName;
public Comsumer (String name){
mName = name;
}
public void run() {
while (true) {
synchronized (mSync) {
if (mStorage.size() == 0) {
try {
mSync.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String product = mStorage.removeFirst();
Log.d(TAG, "Consumer " + mName + " consume " + product + " storage size:" + mStorage.size());
mSync.notify();
Thread.yield();
}
}
}
}
这样调用:
new Producer().start();
new Comsumer("herbert").start();
new Comsumer("wellings").start();
OK, 运行,
第一次没问题,都被herbert消费了,
第二次也没问题,wellings也消费了10个,
第三次,程序挂了。
W/dalvikvm( 3552): threadid=14: thread exiting with uncaught exception (group=0x40a4f300) E/AndroidRuntime( 3552): FATAL EXCEPTION: Thread-694 E/AndroidRuntime( 3552): java.util.NoSuchElementException E/AndroidRuntime( 3552): at java.util.LinkedList.removeFirstImpl(LinkedList.java:689) E/AndroidRuntime( 3552): at java.util.LinkedList.removeFirst(LinkedList.java:676) E/AndroidRuntime( 3552): at com.example.producercomsumer.MainActivity$Comsumer.run(MainActivity.java:85) W/ActivityManager( 1848): Force finishing activity com.example.producercomsumer/.MainActivity
什么情况?
加了打印消息后看得更清楚:
String product = null;
try {
product = mStorage.removeFirst();
} catch (Exception e) {
e.printStackTrace();
}
Log.d(TAG, "Consumer " + mName + " consume " + product + " storage size:" + mStorage.size());
原来是wellings试图去空仓库里拿东西,不是有if (仓库为空) wait(); 吗?
答案是有两个消费线程被notify,只有先被nogify的那个线程可以拿到最后一个,后面的线程也被notify到了(说明一个问题:notify()会叫醒所有wait()的线程,相当于notifyAll()?) 但是拿不东西就会出错。
现在才发现while() 和 if()的区别
while()被notify到后,会循环到前面再检测一次仓库是否为空,而if()不会再次检测,这就是为什么我看到的代码都是用while的原因。