ero*_*ras 5 java concurrency multithreading
我有两个线程,它们都访问Vector.t1添加一个随机数,而t2删除并打印第一个数字.下面是代码和输出.t2似乎只执行一次(在t1开始之前)并永远终止.我在这里错过了什么吗?(PS:同样使用ArrayList测试)
import java.util.Random;
import java.util.Vector;
public class Main {
public static Vector<Integer> list1 = new Vector<Integer>();
public static void main(String[] args) throws InterruptedException {
System.out.println("Main started!");
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("writer started! ");
Random rand = new Random();
for(int i=0; i<10; i++) {
int x = rand.nextInt(100);
list1.add(x);
System.out.println("writer: " + x);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("reader started! ");
while(!list1.isEmpty()) {
int x = list1.remove(0);
System.out.println("reader: "+x);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
t2.start();
t1.start();
t1.join();
t2.join();
}
Run Code Online (Sandbox Code Playgroud)
输出:主要开始了!读者开始了!作家开始了!作者:40位作家:9位作家:23位作家:5位作家:41位作家:29位作家:72位作家:73位作家:95位作家:46位
这听起来像是一个理解并发的玩具,所以我之前没有提到它,但我现在会提到(在顶部,因为它很重要)。
如果这是生产代码,请不要自行编写。java.util.concurrent 中有大量实现良好(已调试)的并发数据结构。使用它们。
消费时,您不需要根据“消耗的所有物品”关闭您的消费者。这是由于竞争条件造成的,即消费者可能会“领先”于生产者并仅因为生产者尚未写入要消费的项目而检测到空列表。
有多种方法可以完成消费者的关闭,但没有一种方法可以通过单独查看要使用的数据来完成。
我的建议是,当生产者完成生产时,生产者向消费者发出“信号”。然后,当消费者收到“信号”、不再产生数据并且列表为空时,它将停止。
替代技术包括创建“关闭”项。“生产者”添加关闭项,消费者只有看到“关闭”项才关闭。如果您有一组消费者,请记住您不应删除关闭项(否则只有一个消费者会关闭)。
此外,消费者可以“监视”生产者,这样,如果生产者“活动/存在”并且列表为空,则消费者假设将有更多数据可用。当生产者死亡/不存在并且没有可用数据时,就会发生关闭。
您使用哪种技术取决于您喜欢的方法以及您想要解决的问题。
我知道人们喜欢优雅的解决方案,但如果您的单一生产者意识到单一消费者,第一个选择看起来像。
public class Producer {
public void shutdown() {
addRemainingItems();
consumer.shutdown();
}
}
Run Code Online (Sandbox Code Playgroud)
消费者看起来像{
public class Consumer {
private boolean shuttingDown = false;
public void shutdown() {
shuttingDown = true;
}
public void run() {
if (!list.isEmpty() && !shuttingDown) {
// pull item and process
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,缺乏对列表中项目的锁定本质上是危险的,但您仅声明了一个消费者,因此不存在从列表中读取的争用。
现在,如果您有多个使用者,则需要提供保护以确保单个项目不会同时被两个线程拉取(并且需要以所有线程关闭的方式进行通信)。