happens-before是保证Memory Visibility的前提,根据javadoc的说明:
The results of a write by one thread are guaranteed to be 可见 to a read by another thread only if the write operation happens-before the read operation。
这句话的意思说,只有当happens-before的时候,一个write operation才对另一个read operation可见。换句话说,就是read operation能够读到write operation的结果。
下面讲解由synchronized
、volatile
、Thread.start()
、Thread.join()
形成的happens-before关系。
不利用任何java.util.concurrent
工具、synchronized
、volatile
关键字时,是无法保证happens-before机制的。
见NoHappensBefore.java的代码:
public class NoHappensBefore {
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(5000L);
stop = true;
}
}
在上面这个例子里,t1
有可能永远都不结束,因为main thread
对stop
变量修改对于t1
不可见。
根据javadoc的说明:
Each action in a thread happens-before every action in that thread that comes later in the program's order.
见InThreadHappensBefore.java的代码:
public class InThreadHappensBefore {
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
stop = true;
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
}
}
这里例子里,stop = true
和while (!stop)
是在一同一个thread中的,所以前者对后者visible。
根据javadoc的说明:
An
unlock
(synchronized
block or method exit) of a monitor happens-before every subsequent lock (synchronized block or method entry) of that same monitor. And because the happens-before relation is transitive, all actions of a thread prior to unlocking happen-before all actions subsequent to any thread locking that monitor.
这里提供了两个例子:
synchronized
修饰的方法保证happens-before的例子:SynchronizedMethodHappensBefore.java
public class SynchronizedMethodHappensBefore {
private static boolean stop = false;
private static synchronized boolean isStop() {
return stop;
}
private static synchronized void markStop() {
stop = true;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (!isStop()) {
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(5000L);
markStop();
}
}
synchronized block
保证happens-before的例子:SynchronizedBlockHappensBefore
public class SynchronizedBlockHappensBefore {
private static boolean stop = false;
private static Object monitor = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (!stop) {
synchronized (monitor) {
// 空的,只是为了获得monitor锁
}
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(5000L);
synchronized (monitor) {
stop = true;
}
}
}
根据javadoc的说明:
A write to a
volatile
field happens-before every subsequent read of that same field. Writes and reads of volatile fields have similar memory consistency effects as entering and exiting monitors, but do not entail mutual exclusion locking.
见VolatileHappensBefore.java的代码:
public class VolatileHappensBefore {
private static volatile boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(5000L);
stop = true;
}
}
stop
变量用了volatile
修饰,因此对stop
变量的修改对于t1
可见。
根据javadoc的说明:
A call to
start
on a thread happens-before any action in the started thread.
见ThreadStartHappensBefore.java的代码:
public class ThreadStartHappensBefore {
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
stop = true;
Thread t1 = new Thread(() -> {
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
}
}
happens-before具有传递性,stop = true
happens-before t1.start()
,t1.start()
happens-before t1中的所有操作,所以stop = true
happens-before t1里的所有动作。
根据javadoc的说明:
All actions in a thread happen-before any other thread successfully returns from a
join
on that thread.
见ThreadJoinHappensBefore.java的代码:
public class ThreadJoinHappensBefore {
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread mainThread = Thread.currentThread();
Thread t1 = new Thread(() -> {
try {
mainThread.join();
} catch (InterruptedException e) {
return;
}
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(5000L);
stop = true;
System.out.println(Thread.currentThread().getName() + " finished");
}
}
注意上面的例子:t1
里mainThread.join()
,当join
方法返回后,main thread
的所有操作都对t1
可见。
根据javadoc的说明:
The methods of all classes in java.util.concurrent and its subpackages extend these guarantees to higher-level synchronization.
下面对每一个进行讲解。
根据javadoc的说明:
Actions in a thread prior to placing an object into any concurrent collection happen-before actions subsequent to the access or removal of that element from the collection in another thread.
见ConcurrentCollectionHappensBefore.java的代码:
public class ConcurrentCollectionHappensBefore {
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
// commonCollectionNeverEnds();
concurrentCollectionWillEnds();
}
private static void concurrentCollectionWillEnds() throws InterruptedException {
ConcurrentLinkedQueue<Integer> conQueue = new ConcurrentLinkedQueue<>();
Thread t1 = new Thread(() -> {
while (!stop) {
conQueue.peek();
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(5000L);
stop = true;
conQueue.add(1);
}
private static void commonCollectionNeverEnds() throws InterruptedException {
Queue<Integer> conQueue = new LinkedList<>();
Thread t1 = new Thread(() -> {
while (!stop) {
conQueue.peek();
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(5000L);
stop = true;
conQueue.add(1);
}
}
在上面这个例子中concurrentCollectionWillEnds
方法利用Concurrent Collection的happens-before特性,让stop
变量的变化对t1
可见。
而普通的Collection不具有happens-before特性,所以stop变量的变化对于t1
依然是invisible。
根据javadoc的说明:
Actions in a thread prior to the submission of a Runnable to an Executor happen-before its execution begins. Similarly for Callables submitted to an ExecutorService.
见ExecutorSubmissionHappensBefore.java的代码:
public class ExecutorSubmissionHappensBefore {
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
stop = true;
executor.submit(() -> {
Thread.currentThread().setName("t1");
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
});
executor.shutdown();
}
}
这个和ThreadStartHappensBefore.java有点类似。
根据javadoc的说明:
Actions taken by the asynchronous computation represented by a Future happen-before actions subsequent to the retrieval of the result via Future.get() in another thread.
public class FutureHappensBefore {
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<?> mainThreadFuture = executor.submit(() -> {
// try ...
TimeUnit.SECONDS.sleep(5L);
// catch ...
stop = true;
System.out.println("main thread future finished");
});
executor.submit(() -> {
Thread.currentThread().setName("t1");
// try ...
mainThreadFuture.get();
// catch ...
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
});
executor.shutdown();
}
}
在这个例子里t1
对mainThreadFuture.get()
,那么mainThreadFuture
对于stop
的修改在Future.get()
返回的时候,对t1
可见。
根据javadoc的说明:
Actions prior to "releasing" synchronizer methods such as
Lock.unlock
,Semaphore.release
, andCountDownLatch.countDown
happen-before actions subsequent to a successful "acquiring" method such asLock.lock
,Semaphore.acquire
,Condition.await
, andCountDownLatch.await
on the same synchronizer object in another thread.
也就是说:
- 在
Lock.unlock
之前的动作,对于Lock.lock
之后的动作可见 - 在
Semaphore.release
之前的动作,对于Semaphore.acquire
之后的动作可见 - 在
CountDownLatch.countDown
之前的动作,对于CountDownLatch.await
之后的动作可见
见LockReleaseHappensBefore.java的代码:
public class LockReleaseHappensBefore {
private static boolean stop = false;
private static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (!stop) {
lock.lock();
// 什么都不做,只是为了获得锁
lock.unlock();
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(5000L);
lock.lock();
stop = true;
lock.unlock();
}
}
这个例子和SynchronizedBlockHappensBefore.java有点像。
根据javadoc的说明:
For each pair of threads that successfully exchange objects via an
Exchanger
, actions prior to theexchange()
in each thread happen-before those subsequent to the correspondingexchange()
in another thread.
见ExchangerHappensBefore.java的代码:
注意t1First
里面,t1
先进入Exchanger.exchange
,main thread
后进入Exchange.exchange
:
private static void t1First() throws InterruptedException {
System.out.println("============ t1First ============");
Exchanger exchanger = new Exchanger();
Thread t1 = new Thread(() -> {
String threadName = Thread.currentThread().getName();
try {
System.out.println(threadName + " await exchanging");
System.out.println(threadName + " exchanged: " + exchanger.exchange("from t1"));
} catch (InterruptedException e) {
return;
}
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.SECONDS.sleep(2L);
stop = true;
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " await exchanging");
System.out.println(threadName + " exchanged: " + exchanger.exchange("from main thread"));
}
而mainThreadFirst
里,main thread
先进入Exchange.exchange
,t1
后进入Exchanger.exchange
:
private static void mainThreadFirst() throws InterruptedException {
System.out.println("============ mainThreadFirst ============");
Exchanger exchanger = new Exchanger();
Thread t1 = new Thread(() -> {
String threadName = Thread.currentThread().getName();
try {
TimeUnit.SECONDS.sleep(2L);
System.out.println(threadName + " await exchanging");
System.out.println(threadName + " exchanged: " + exchanger.exchange("from t1"));
} catch (InterruptedException e) {
return;
}
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
TimeUnit.SECONDS.sleep(1L);
stop = true;
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " await exchanging");
System.out.println(threadName + " exchanged: " + exchanger.exchange("from main thread"));
}
从上面可以看到,谁先谁后都不影响stop
对t1
可见,所以Exchanger.exchange()
的happens-before具有对称性。
也就是说对于同一个Exchanger
所涉及到的两个线程,其中任意一个线程中在exchange()
之前的操作,都对另一个线程中在exchange()
之后的操作可见。
根据javadoc的说明:
Actions prior to calling
CyclicBarrier.await
andPhaser.awaitAdvance
(as well as its variants) happen-before actions performed by the barrier action, and actions performed by the barrier action happen-before actions subsequent to a successful return from the correspondingawait
in other threads.
也就是说:
CyclicBarrier.await
之前的action happens-before barrier action- barrier action happens-before
CyclicBarrier.await
之后的action
见CyclicBarrierHappensBefore.java的代码:
先看一段第1点对应的代码:
private static void actionBeforeAwaitHappensBeforeBarrierAction() throws BrokenBarrierException, InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(1, () -> {
while (!stop) {
}
System.out.println(Thread.currentThread().getName() + " finished");
});
Thread t1 = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2L);
stop = true;
System.out.println(Thread.currentThread().getName() + " await barrier");
barrier.await();
} catch (InterruptedException e) {
return;
} catch (BrokenBarrierException e) {
return;
}
}, "t1");
t1.start();
}
从上面的代码可以看到,在t1 thread
里我们在它await
之前stop = true
,然后在barrier action
里读取stop
变量。此时stop
变量对于barrier action
是可见的。
和第2点对应的代码:
private static void barrierActionHappensBeforeActionAfterAwait() throws InterruptedException, BrokenBarrierException {
CyclicBarrier barrier = new CyclicBarrier(1, () -> stop = true);
Thread t1 = new Thread(() -> {
String threadName = Thread.currentThread().getName();
try {
System.out.println(threadName + " await barrier");
while (!stop) {
barrier.await();
}
} catch (InterruptedException e) {
return;
} catch (BrokenBarrierException e) {
return;
}
System.out.println(Thread.currentThread().getName() + " finished");
}, "t1");
t1.start();
}
在上面的代码里,barrier action
对stop = true
,然后在t1 thread
里await
,此时stop
变量对于t1 thread
是可见的。
需要注意的是,happens-before不能解决Thread interference的问题。
见HappensBeforeThreadInterference.java的代码,其内部使用了VolatileCounter:
public class VolatileCounter implements Counter {
private volatile int count = 0;
@Override
public void increment() {
count++;
}
@Override
public void decrement() {
count--;
}
@Override
public int value() {
return count;
}
}
VolatileCounter#counter
使用了volatile
修饰,虽然这能解决memory visibility问题,但是依然解决不了Thread interference问题,运行的结果很大概率情况下不是0。