JUC实战讲解-1-李贺飞老师

【来自B站JUC视频:https://www.bilibili.com/video/BV14W411u7gB】尚硅谷JUC源码讲授实战教程完整版(java juc线程精讲)

1、Java JUC 简介

在JDK5提供了 java.util.concurrent (简称 JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。 提供可调的、灵活的线程池。还提供了设计用于 多线程上下文中的 Collection 实现等。

2、volatile 关键字-内存可见性

JVM为每一个线程提供一个独立的缓存,用于提高效率。这个缓存,是每个线程私有的,称之为“本地内存”。内存可见性(Memory Visibility),是指当一个线程修改了某个共享变量的值之后,其他线程要能够立即知道这个修改。可见性错误,是指当读操作与写操作处在不同的线程中执行时,我们无法确保执行读操作的线程能实时看到其他写线程新写入的值。我们可以通过同步来保证对象被安全地发布。除此之外,我们也可以使用一种更加轻量级的volatile变量。Java 提供了一种稍弱的同步机制,即 volatile 变量,用来确保将变量的更新操作能够立即通知到其他线程。

可以将 volatile 看做一个轻量级的锁,但是又与锁有些不同:

(1)对于多线程,它不是一种互斥关系。(2)它只保证了“内存可见性”,不能够保证变量状态的 “原子性操作”。

public class VolatileTest {
    public static void main(String[] args) {
        ThreadDemo t = new ThreadDemo();
        new Thread(t).start();
        while (true) {
            if(t.isFlag()) {
                System.out.println("------");
                break;
            }
        }
    }
}
class ThreadDemo implements Runnable {
    private boolean flag = false;
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;
        System.out.println("flag = " + isFlag());
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

        

mian线程读取到的flag 一直都是false,所以打印结果为 flag = true,然后程序没有结束。

解决:volatile,当多个线程进行操作共享数据时,可以保证内存中的数据可见性。

public class VolatileTest {
    public static void main(String[] args) {
        ThreadDemo t = new ThreadDemo();
        new Thread(t).start();
        while (true) {
            if(t.isFlag()) {
                System.out.println("------");
                break;
            }
        }
    }
}
class ThreadDemo implements Runnable {
    private volatile boolean flag = false;
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;
        System.out.println("flag = " + isFlag());
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

3、原子变量-CAS算法

类 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的实例各自提供对 相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。

AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 类进一步扩展了原子操 作,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方 面也引人注目,这对于普通数组来说是不受支持的。

核心方法:boolean compareAndSet(expectedValue, updateValue)

java.util.concurrent.atomic 包下提供了一些原子操作的常用类: AtomicBoolean 、AtomicInteger 、AtomicLong 、 AtomicReference AtomicIntegerArray 、AtomicLongArray ,AtomicMarkableReference ,AtomicReferenceArray ,AtomicStampedReference
具体的一些方法,可查看API文档【https://www.matools.com/api/java8】。

i++ 原子性问题,先读取到i 然后再 ++ ,操作被分开了,有同步安全问题

public class AtomicTest {
    public static void main(String[] args) {
        Atomic a = new Atomic();
        for (int i = 0; i < 10; i++) {
            new Thread(a).start();
        }
    }
}
class Atomic implements Runnable {
    private volatile int serialNumber = 0;
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber());
    }
    public int getSerialNumber() {
        return serialNumber++;
    }
}

原子变量JDK5后 java.util.concurrent.atomic包下提供了常用的原子变量。

(1)volatile 保证内存可见性

(2)CAS算法 保证数据的原子性

CAS算法是硬件对于并发操作共享数据的支持:包含了三个操作数:内存值 V、预估值 A、更新值 B:当且仅当 V == A时,V = B,否则,将不做任何操作。

public class CompareAndSwapTest {
    public static void main(String[] args) {
        CompareAndSwap cas = new CompareAndSwap();
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int expectedValue = cas.get();
                    boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
                    System.out.println(b);
                }
            }).start();
        }
    }
}
class CompareAndSwap {
    private int value;
    // 获取内存值
    public synchronized int get(){
        return value;
    }
    // 比较
    public synchronized int compareAndSwap(int expectedValue, int newValue) {
        int oldValue = value;
        if(oldValue == expectedValue) {
            this.value = newValue;
        }
        return oldValue;
    }
    // 设置
    public synchronized boolean compareAndSet(int expectedValue, int newValue) {
        return expectedValue == compareAndSwap(expectedValue, newValue);
    }
}


public class CopyOnWriteArrayListTest {
    public static void main(String[] args) {
        HelloThread ht = new HelloThread();
        for (int i = 0; i < 2; i++) {
            new Thread(ht).start();
        }
    }
}
/**
 * CopyOnWriteArrayList写入并复制,添加操作多时,效率低,因为每次添加时都会进行复制,开销很大
 * 并发迭代操作多时可以选择
 */
class HelloThread implements Runnable {
    // 这种会出现并发修改异常
//    private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
    private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
    static {
        list.add("AA");
        list.add("BB");
        list.add("CC");
    }
    @Override
    public void run() {
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
            list.add("AA");
        }
    }
}

public class CountDownLatchTest {
    public static void main(String[] args) {
        // 5 表示其他线程的数量
        CountDownLatch latch = new CountDownLatch(5);
        LatchDemo ld = new LatchDemo(latch);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5; i++) {
            new Thread(ld).start();
        }
        try {
            // 此处要一直等到 latch的值为0 ,就能往下执行了
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("消耗时间为:" + (end - start));
    }
}
class LatchDemo implements Runnable {
    private CountDownLatch latch;
    public LatchDemo(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        synchronized(this) {
            try {
                for (int i = 0; i < 100; i++) {
                    if (i % 2 == 0) {
                        System.out.println(i);
                    }
                }
            } finally {
                latch.countDown();
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/cmm0401/article/details/108926071