package com.haogrgr.test.main; import java.util.ArrayList; import java.util.List; public class ReorderTest { private static List<String> l = new ArrayList<>(); public static void main(String[] args) { Thread[] threads = new Thread[20]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread(new Runnable() { @Override public void run() { while (true) { synchronized (ReorderTest.class) { if (l.size() < 1024) l.add("ttt"); } } } }); threads[i].start(); } Thread t1 = new Thread() { @SuppressWarnings("unused") public void run() { while (true) { //l加上volatile即可解决,利用了volatile获取语义 //JMM的final语义 :1在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。 //2初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。 /** * 曾经天真的以为JDK8已经修复了上述问题,其实没有,是因为看错了逻辑,如果initialCapacity == * 0时确实没有上面的问题, * 因为EMPTY_ELEMENTDATA是一个final类型的依据JMM可以保证, * 调用add的线程在拿到的arraylist的引用是已经初始化好的,加上 * volatile即可避免此错误,利用了volatile的获取语义 */ List<String> local = l; l = new ArrayList<String>(1024); } } }; t1.start(); } }