Java 多线程同步问题求助

2019-06-07 23:18:44 +08:00
 flowaters

想用 wait/notify 和 map 来实现一个线程写入,另一个线程随后读出的同步处理。

但是遇到了 happens-before 问题。

一个可以复现的代码如下:

import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainConcurrentHashmapTest {

	private static ExecutorService executorService = Executors.newFixedThreadPool(2);

	public static void main(String[] args) {

		for (int i = 0; i != 100000; ++i) {
			test();
		}

		executorService.shutdown();
	}

	public static void test() {

		final Map<String, Object> map = new ConcurrentHashMap<String, Object>();

		final Object mutex = new Object();
		final String key = UUID.randomUUID().toString();

		Runnable consumer = new Runnable() {

			@Override
			public void run() {
				synchronized (mutex) {
					try {
						mutex.wait(10000L);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					Object value = map.remove(key);
					if (value == null) {
						System.out.println("got null value for key = " + key);
					}
				}
			}
		};

		Runnable producer = new Runnable() {

			@Override
			public void run() {
				map.put(key, LocalDateTime.now().toString());
				synchronized (mutex) {
					mutex.notify();
				}
			}
		};

		executorService.submit(consumer);
		executorService.submit(producer);
	}

}

运行的时候会发现,有时候得不到最新的 value 值,如下:

got null value for key = f972a078-c314-4cd1-a5e4-d3ce11195a7e
got null value for key = 6ef9476c-8ded-43ec-900e-5f859eeaec1e
got null value for key = 8ecff482-eb9a-4e63-8eee-c950a485fbb0

为什么呢?有什么改进建议么?

2815 次点击
所在节点    Java
7 条回复
chendy
2019-06-07 23:45:10 +08:00
线程池执行任务的顺序不一定是提交的顺序,consumer 的方法可能会在 producer 之前执行,所以会取到 null
momocraft
2019-06-07 23:51:13 +08:00
沒有東西保證 consumer 比 producer “隨後” 獲得鎖

工具就比較多了 比如 CountdownLatch
springmarker
2019-06-08 00:22:26 +08:00
BlockingQueue 不就好了
mejee
2019-06-08 01:16:14 +08:00
楼上说的对,还有一个问题,根据代码,不停地 submit 这两个 runable,很可能在某一次执行中,producer 先 notify,这就导致 consumer 会 wait 10000 ms,造成程序卡顿。还有,你每次 test 方法运行,用的是不同的 mutex,根据前面的情况,这就可能导致两个 consumer 同时霸占两个线程,直到 wait 等待超时,造成程序卡顿,幸好你 wait 带了个时间参数,否则你运行这么多次的 test 方法,非常有可能这个线程池停在那,导致你程序一直不执行下去
NewDraw
2019-06-08 08:49:30 +08:00
你的需求是阻塞队列。
johnniang
2019-06-08 13:23:31 +08:00
建议先看懂#生产者消费者模型#。
dailiha01sy
2019-06-09 01:35:37 +08:00
用 Exchange

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/571872

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX