多线程——并发编程三大特性

2022/2/9 14:42:35

本文主要是介绍多线程——并发编程三大特性,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

并发编程三大特性

1、可见性:各线程之间对共享变量的可见性,即一个线程更改了共享变量的值,其他线程也能看到并更新到自己线程中的值。共享资源一般都放在堆空间(主内存),每个线程使用公共资源都会将公共资源拷贝一份到自己的线程中(本地缓存),当一个线程对共享资源进行更改并写回到堆空间,而其他线程不知道共享资源已经被修改了。
Volatile:使用Volatile修饰共享变量(非引用类型),当一个线程对共享变量进行更改,会让其他线程都会读到变量的更改值。使用volatile,将会强制所有线程都去堆内存中读取running的值。
synchronized:可保证可见性,可以触发本地缓存和主内存之间的数据同步。
2、有序性:为了提高执行效率,程序在编译或者运行的时候会对代码进行指令重排。在单线程中保证最终一致性,指令重排并不会产生问题,但是在多线程的情况下便可能产生不希望看到的结果。使用volatile修饰的内存,不可以重排序,对volatile修饰变量的读写访问,都不可以换顺序。
3、原子性:一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。原子性就像数据库里面的事务一样,他们是一个团队,同生共死。加锁、AtomicXXX原子操作。

缓存行对齐:缓存行64个字节是CPU同步的基本单位,缓存行隔离会比伪共享效率要高
何为缓存行?
程序运行是靠CPU执行主存中代码,但是CPU和主存的速度差异是非常大的,为了降低这种差距,便使用了CPU缓存,现在的计算机中普遍使用了缓存,分为一级缓存,二级缓存,还有一些具备三级缓存。而每个缓存里面都是由缓存行组成的,缓存系统中是以缓存行(cache line)为单位存储的。缓存行的大小都是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享

缓存行对齐的编程技巧:

public class CacheLinePadding {
    public static long COUNT = 10_0000_0000L;
    private static class T {
        private long p1, p2, p3, p4, p5, p6, p7;
        public long x = 0L;
        private long p9, p10, p11, p12, p13, p14, p15;
    }
    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);
    }
}

JDK8引入了@sun.misc.Contended注解,来保证缓存行隔离效果 要使用此注解,必须去掉限制参数:-XX:-RestrictContended

//注意:运行这个小程序的时候,需要加参数:-XX:-RestrictContended
public class T05_Contended {
    public static long COUNT = 10_0000_0000L;
    private static class T {
        @Contended  //只有1.8起作用 , 保证x位于单独一行中
        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);
    }
}


这篇关于多线程——并发编程三大特性的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程