加入收藏 | 设为首页 | 会员中心 | 我要投稿 汽车网 (https://www.0577qiche.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 教程 > 正文

Java 多线程二:并发编程的三大特性

发布时间:2023-04-15 11:23:51 所属栏目:教程 来源:
导读:所谓线程数据的可见性,指的就是内存中的某个数据,假如第一个 cpu 的一个核读取到了,和其他的核读取到这个数据之间的可见性。

每个线程会保存一份拷贝到线程本地缓存,使用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;
    }
}
 

(编辑:汽车网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章