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)