Java 多线程二:并发编程的三大特性
发布时间:2023-04-15 11:23:51 所属栏目:教程 来源:
导读:所谓线程数据的可见性,指的就是内存中的某个数据,假如第一个 cpu 的一个核读取到了,和其他的核读取到这个数据之间的可见性。
每个线程会保存一份拷贝到线程本地缓存,使用volatile,可以保持线程之间数据可见性
每个线程会保存一份拷贝到线程本地缓存,使用volatile,可以保持线程之间数据可见性
|
所谓线程数据的可见性,指的就是内存中的某个数据,假如第一个 cpu 的一个核读取到了,和其他的核读取到这个数据之间的可见性。 每个线程会保存一份拷贝到线程本地缓存,使用volatile,可以保持线程之间数据可见性。 如下示例 package git.snippets.juc; import java.util.concurrent.TimeUnit; /** * 并发编程三大特性之:可见性 * * @author <a href="mailto:">Grey</a> * @since 1.8 */ public class ThreadVisible { static volatile boolean flag = true; public static void main(String[] args) throws Exception { Thread t = new Thread(() -> { System.out.println(Thread.currentThread() + " t start"); while (flag) { // 如果这里调用了System.out.println() // 会无论flag有没有加volatile,数据都会同步 // 因为System.out.println()背后调用的synchronized // System.out.println(); } System.out.println(Thread.currentThread() + " t end"); }); t.start(); TimeUnit.SECONDS.sleep(3); flag = false; // volatile修饰引用变量 new Thread(a::m, "t2").start(); TimeUnit.SECONDS.sleep(2); a.flag = false; // 阻塞主线程,防止主线程直接执行完毕,看不到效果 system.in.read(); } private static volatile A a = new A(); static class A { volatile boolean flag = true; void m() { System.out.println("m start"); while (flag) { } System.out.println("m end"); } } } 代码说明: volatile修饰了flag变量,主线程改了flag的值,子线程可以感知到; 如在上述代码的死循环中增加了System.out.println(), 则会强制同步flag的值,无论flag本身有没有加volatile; 如果volatile修饰一个引用对象,如果对象的属性(成员变量)发生了改变,volatile不能保证其他线程可以观察到该变化。 关于三级缓存 3_cache 如上图,内存读出的数据会在 L3,L2,L1 上都存一份。 在从内存中读取数据的时候,根据的是程序局部性的原理,按块来读取,这样可以提高效率,充分发挥总线 cpu 针脚等一次性读取更多数据的能力。 所以这里引入了一个缓存行的概念,目前一个缓存行多用64个字节来表示。 如何来验证 cpu 读取缓存行这件事,我们可以通过一个示例来说明: package git.snippets.juc; /** * 缓存行对齐 * * @author <a href="mailto:">Grey</a> * @since 1.8 */ public class CacheLinePadding { public static T[] arr = new T[2]; static { arr[0] = new T(); arr[1] = new T(); } public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { for (long i = 0; i < 1000_0000L; i++) { arr[0].x = i; } }); Thread t2 = new Thread(() -> { for (long i = 0; i < 1000_0000L; i++) { arr[1].x = i; } }); final long start = System.nanoTime(); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println((System.nanoTime() - start) / 100_0000); System.out.println("arr[0]=" + arr[0].x + " arr[1]=" + arr[1].x); } private static class Padding { public volatile long p1, p2, p3, p4, p5, p6, p7; } // T这个类extends Padding与否,会影响整个流程的执行时间,如果继承了,会减少执行时间, // 因为继承Padding后,arr[0]和arr[1]一定不在同一个缓存行里面,所以不需要同步数据,速度就更快一些了。 private static class T /*extends Padding*/ { public volatile long x = 0L; } } (编辑:汽车网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
推荐文章
站长推荐
