多线程环境中javadeque的奇怪行为
我编写了一个简单的演示代码来测试“守护进程线程如何工作”。但演示显示了另一种奇怪的行为: 我制作一个多线程环境中javadeque的奇怪行为,java,multithreading,Java,Multithreading,我编写了一个简单的演示代码来测试“守护进程线程如何工作”。但演示显示了另一种奇怪的行为: 我制作一个Deque来保存名为Event的元素,并为两个工作线程共享它,一个线程将元素添加到Deque。再次检查Deque的大小并移除3秒前创建的元素。奇怪的是,调用Deque的size()总是返回0。我知道ArrayDeque和linkedeque不是线程安全的,但我可以解决这样的奇怪问题: 1、 将Deque的实现更改为ConcurrentLinkedque。 2、 同步了deque实例。 3、 在初始
Deque
来保存名为Event的元素,并为两个工作线程共享它,一个线程将元素添加到Deque。再次检查Deque的大小并移除3秒前创建的元素。奇怪的是,调用Deque的size()
总是返回0。我知道ArrayDeque
和linkedeque
不是线程安全的,但我可以解决这样的奇怪问题:
1、 将Deque的实现更改为ConcurrentLinkedque。
2、 同步了deque实例。
3、 在初始化清理器之前,将一个元素放入共享deque。
4、 检查尺寸,然后打印出来。
所有这些都很有效,这很奇怪,我不知道为什么
以下是演示代码和我的运行时环境:
java版本“1.8.0_141”
Java(TM)SE运行时环境(build 1.8.0_141-b15)
Java HotSpot(TM)64位服务器虚拟机(构建25.141-b15,混合模式)
MacBook Pro(13英寸,2016年,两个Thunderbolt 3端口)
MacOS Catalina 10.15.6
问题是:3和4的修正,让我非常惊讶,因为我认为这是一种非常奇怪的修正方法。而且,虽然ArrayQue不是线程安全的,但是当写入程序停止时,清理程序仍然会得到size()返回0,当实际只有一个线程正在运行时(主线程除外),它的工作原理就像清理程序的deque是最终的和不可变的。关于并发性的一个奇怪的事情是内存模型。如果不同步,两个线程可能有不同的数据“副本”或“视图”。所以,这就是为什么您将大小视为0。这与直觉相反,因为你可以认为它们指向同一个东西,但事实并非如此 这里有更详细的信息:
好消息是你知道如何解决它 另一个修复方法:如果在Cleaner中放置调试器,它将很好地工作(我忘记了事件类,但它很简单,只有两个字段,一个是
String
type name,另一个是LocalTime
type time for create time。因此,您观察到,当被多个线程访问时,被记录为线程不安全的类是不可靠的,除非您自己强制执行线程安全。您对此感到惊讶吗你已经知道,ArrayDeque
不是线程安全的,那么问题是什么呢?3和4修复,让我非常惊讶。因为我认为这是一种非常奇怪的修复方法。虽然,ArrayDeque
不是线程安全的,但是当写入程序停止时,清洁器仍然会得到大小()返回0,此时实际只有一个线程正在运行(主线程除外)。非常感谢,这非常令人惊讶。我一直认为共享数据是相同的。但实际不是。我将阅读此内容,再次感谢。
public class CleanerTask extends Thread {
private transient Deque<Event> deque;
private AtomicInteger doRemoveTimes = new AtomicInteger(0);
public AtomicInteger getDoRemoveTimes() {
return doRemoveTimes;
}
public CleanerTask(Deque<Event> deque) {
this.deque = deque;
setDaemon(true);
}
@Override
public void run() {
//System.out.println("Cleaner: watch deque " + deque);
while (true) {
clean();
}
}
private void clean() {
//fix 2
/*synchronized (deque) {
if(deque.size() == 0) {
return;
}
}*/
if (deque.size() == 0) {
//System.out.println("Cleaner: deque's size:" + deque.size());//fix 3
return;
}
int removes = 0;
int beforeNext;
do {
beforeNext = removes;
Event event = deque.getLast();
if (Duration.between(event.getTime(), LocalTime.now()).getSeconds() > 3) {
deque.removeLast();
System.out.println(event + " is removed");
removes++;
}
} while (removes > beforeNext && deque.size() > 0);
if (removes > 0) {
doRemoveTimes.addAndGet(removes);
System.out.printf("Cleaner: cleaned %d, remained %d\n", removes, deque.size());
}
}
}
public class WriterTask implements Runnable {
private Deque<Event> deque;
public WriterTask(Deque<Event> deque) {
this.deque = deque;
}
@Override
public void run() {
System.out.println(LocalTime.now() + "-" + Thread.currentThread().getId() + ": start write event to the deque: " + deque);
for(int i = 0; i < 10; i++) {
Event event = new Event("event generated by " + Thread.currentThread().getId());
deque.addFirst(event);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(LocalTime.now() + "-" + Thread.currentThread().getId() + ": finished");
}
}
public class DaemonCleanMain {
public static void main(String[] args) {
//Deque<Event> deque = new ConcurrentLinkedDeque<>();//fix 1
Deque<Event> deque = new ArrayDeque<>();
WriterTask writer = new WriterTask(deque);
int i = args.length > 0 ? Integer.parseInt(args[0]) : 1;
while (i > 0) {
Thread thread = new Thread(writer);
thread.start();
i--;
}
//fix 4
/* try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
CleanerTask cleaner = new CleanerTask(deque);
cleaner.start();
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("size of deque: " + deque.size());
System.out.println("Cleaner is work? " + cleaner.getDoRemoveTimes());
}
}
}
public class Event {
public Event(String name) {
this.name = name;
this.time = LocalTime.now();
}
public Event(String name, LocalTime time) {
this.name = name;
this.time = time;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalTime getTime() {
return time;
}
public void setTime(LocalTime time) {
this.time = time;
}
private String name;
private LocalTime time;
@Override
public String toString() {
return "Event{" +
"name='" + name + '\'' +
", time=" + time +
'}';
}
}