1、可见性(visibility)
volatile保证线程可见性
/**
* volatile 关键字,使一个变量在多个线程间可见
* A B线程都用到一个变量,java默认是A线程中保留一份copy,这样如果B线程修改了该变量,则A线程未必知道
* 使用volatile关键字,会让所有线程都会读到变量的修改值
* <p>
* 在下面的代码中,running是存在于堆内存的t对象中
* 当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个copy,并不会每次都去
* 读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行
* <p>
* 使用volatile,将会强制所有线程都去堆内存中读取running的值
* volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized
*
* @author mashibing
*/
package com.mashibing.juc.c_001_01_Visibility;
import com.mashibing.util.SleepHelper;
import java.io.IOException;
public class T01_HelloVolatile {
private static /*volatile*/ boolean running = true;
private static void m() {
System.out.println("m start");
while (running) {
System.out.println("hello");
}
System.out.println("m end!");
}
public static void main(String[] args) throws IOException {
new Thread(T01_HelloVolatile::m, "t1").start();
SleepHelper.sleepSeconds(1);
running = false;
System.in.read();
}
}
按块读取
缓存行 64字节 缓存行对齐


不需要强行使用缓存行,使用注解@Contended
package com.mashibing.juc.c_001_02_FalseSharing;
import sun.misc.Contended;
//注意:运行这个小程序的时候,需要加参数:-XX:-RestrictContended
import java.util.concurrent.CountDownLatch;
public class T05_Contended {
public static long COUNT = 10_0000_0000L;
//@Contended //只有1.8起作用 , 保证x位于单独一行中
private static class T {
public long x = 0L;
}
public static T[] arr = new T[2];
static {
arr[0] = new T();
arr[1] = new T();
}
public static void main(String[] args) throws Exception {
CountDownLatch latch = new CountDownLatch(2);
Thread t1 = new Thread(() -> {
for (long i = 0; i < COUNT; i++) {
arr[0].x = i;
}
latch.countDown();
});
Thread t2 = new Thread(() -> {
for (long i = 0; i < COUNT; i++) {
arr[1].x = i;
}
latch.countDown();
});
final long start = System.nanoTime();
t1.start();
t2.start();
latch.await();
System.out.println((System.nanoTime() - start) / 100_0000);
}
}
设置参数

缓存一致性协议:
比较出名的有mesi
被修改 独享 共享 失效


2、有序性(ordering)
程序验证乱序:
package com.mashibing.juc.c_001_03_Ordering;
/**
* 本程序跟可见性无关,曾经有同学用单核也发现了这一点
*/
import java.util.concurrent.CountDownLatch;
public class T01_Disorder {
private static int x = 0, y = 0;
private static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
for (long i = 0; i < Long.MAX_VALUE; i++) {
x = 0;
y = 0;
a = 0;
b = 0;
CountDownLatch latch = new CountDownLatch(2);
Thread one = new Thread(new Runnable() {
public void run() {
a = 1;
x = b;
latch.countDown();
}
});
Thread other = new Thread(new Runnable() {
public void run() {
b = 1;
y = a;
latch.countDown();
}
});
one.start();
other.start();
latch.await();
String result = "第" + i + "次 (" + x + "," + y + ")";
if (x == 0 && y == 0) {
System.err.println(result);
break;
}
}
}
}
乱序的原因:cpu为了增加效率,做了优化
乱序的条件:不影响单线程的最终一直性
例子:对象版初始化
3、原子性(atomicity)
Java并发编程:理解volatile、可见性和有序性
本文探讨了Java并发中的关键概念:volatile保证线程可见性,如何避免false sharing并通过@Contended注解控制内存布局,以及程序的有序性原理。通过实例分析了可见性与原子性的区别,以及为何单线程程序仍可能观察到乱序执行。

被折叠的 条评论
为什么被折叠?



