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

    科技2022-08-05  122

    【来自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(); } } } }

     

     

     

     

     

     

     

     

     

    Processed: 0.035, SQL: 8