写作时间:2020-02-14
实现目标:JUC解析与总结
涉及知识:JUC

什么是JUC

进程和线程

进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。

线程状态

public enum State {
    // 新生
    NEW,
    // 运行
    RUNNABLE,
    // 阻塞
    BLOCKED,
    // 等待,死死地等
    WAITING,
    // 超时等待
    TIMED_WAITING,
    // 终止
    TERMINATED;
}

并发和并行

并发:同一时刻多个线程在访问同一个资源,多个线程对一个点

并行:多项工作一起执行,之后再汇总

Lock锁

Synchronized 版

package com.yan;

public class SaleTicket {
    public static void main(String[] args) {
        // 线程   操作  资源类
        Ticket ticket = new Ticket();

        new Thread(() -> {
            for (int i = 1; i < 30; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 1; i < 30; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 1; i < 30; i++) {
                ticket.sale();
            }
        }, "C").start();
    }
} //资源类
class Ticket {

    private int number = 30;

    // 卖票的方式
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出第" + (number --) + "张票,  剩余:" + number);
        }
    }
}

Lock版

package com.yan;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SaleTicket {
    public static void main(String[] args) {
// 并发:多线程操作同一个资源类, 把资源类丢入线程
        Ticket ticket = new Ticket();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
        new Thread(()->{for (int i = 1; i < 40 ; i++)
            ticket.sale();},"A").start();
        new Thread(()->{for (int i = 1; i < 40 ; i++)
            ticket.sale();},"B").start();
        new Thread(()->{for (int i = 1; i < 40 ; i++)
            ticket.sale();},"C").start();
    }
}
// 1、 new ReentrantLock();
// 2、 lock.lock(); // 加锁
// 3、 finally=> lock.unlock(); // 解锁
class Ticket {
    // 属性、方法
    private int number = 30;
    Lock lock = new ReentrantLock();
    public void sale(){
        lock.lock(); // 加锁
        try {
            // 业务代码
            if (number>0){
                System.out.println(Thread.currentThread().getName()+"卖出第"+
                        (number--)+"张票,剩余:"+number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); // 解锁
        }
    }
}

Synchronized 和 Lock 区别

1、Synchronized 内置的Java关键字, Lock 是一个Java接口
2、Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
3、Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
4、Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以自己设置);
5、Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!

生产者消费者问题

Synchronized 版

package com.yan.pc;

public class SynchronizedPC {

    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}
//判断等待,业务,通知
class Data { // 数字 资源类
    private int number = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
        if (number != 0) { //0
            // 等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        // 通知
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
//        if (number == 0) { // 存在虚假唤醒if 改为 while 判断
        while (number == 0) {
            // 等待
            this.wait();
        } number--;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        // 通知
        this.notifyAll();
    }
}

Lock版

package com.yan.pc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockPC {

    public static void main(String[] args) {
        Data data = new Data();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        },"A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        },"B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        },"C").start();

    }
}

//资源类
class Data{
    //Condition 精准的通知和唤醒线程
    private int num = 1;
    private Lock lock = new ReentrantLock();
    private Condition A = lock.newCondition();
    private Condition B = lock.newCondition();
    private Condition C = lock.newCondition();

    public void printA(){

        lock.lock();
        try {
            while (num != 1){
                //等待
                A.await();
            }
            System.out.println(Thread.currentThread().getName()+" => AAAAAAAAA");
            num = 2;
            //唤醒
            B.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printB(){

        lock.lock();
        try {
            while (num != 2){
                //等待
                B.await();
            }
            System.out.println(Thread.currentThread().getName()+" => BBBBBBBBB");
            num = 3;
            //唤醒
            C.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printC(){

        lock.lock();
        try {
            while (num != 3){
                //等待
                C.await();
            }
            System.out.println(Thread.currentThread().getName()+" => CCCCCCCCC");
            num = 1;
            //唤醒
            A.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

}

集合类不安全

List不安全

package com.yan.unsafe;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

// java.util.ConcurrentModificationException 并发修改异常!
public class ListUnsafe {
    public static void main(String[] args) {
        // 并发下 ArrayList 不安全
        /**
         * 解决方案;
         * 1、List<String> list = new Vector<>();
         * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());
         * 3、List<String> list = new CopyOnWriteArrayList<>();
         */
        // CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略;
        // 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
        // 在写入的时候避免覆盖,造成数据问题!
        List<String> list = new ArrayList<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

set不安全

package com.yan.unsafe;

import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

public class SetUnsafe {
    public static void main(String[] args) {
        // Set<String> set = new HashSet<>();
        // Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}

map不安全

package com.yan.unsafe;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class MapUnsafe {
    public static void main(String[] args) {
        // Map<String, String> map = new HashMap<>();
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(
                        0, 5));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }
}

Callable

package com.yan.callable;

import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class CallableDemo {

    /**
     *
     在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,
     当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。

     一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。

     仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,
     就不能再重新开始或取消计算。get方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,
     然后会返回结果或者抛出异常。

     只计算一次get方法放到最后
     */
    public static void main(String[] args) throws Exception {

        FutureTask<Integer> futureTask = new FutureTask(()->{
            System.out.println(Thread.currentThread().getName()+"  come in callable");
            TimeUnit.SECONDS.sleep(4);
            return 1024;
        });
        FutureTask<Integer> futureTask2 = new FutureTask(()->{
            System.out.println(Thread.currentThread().getName()+"  come in callable");
            TimeUnit.SECONDS.sleep(4);
            return 2048;
        });

        new Thread(futureTask,"zhangsan").start();
        new Thread(futureTask2,"lisi").start();

        //System.out.println(futureTask.get());
        //System.out.println(futureTask2.get());
        //1、一般放在程序后面,直接获取结果
        //2、只会计算结果一次

        while(!futureTask.isDone()){
            System.out.println("wait");
        }
        System.out.println(futureTask.get());
        System.out.println(Thread.currentThread().getName()+" come over");
    }
}

常用辅助工具类

CountDownLatch

package com.yan.util;

import java.util.concurrent.CountDownLatch;

public class CountDonwLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        // 总数是6,必须要执行任务的时候,再使用!
        for (int i = 1; i <= 6 ; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"=> go out");
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }

        countDownLatch.await();

        System.out.println("close door");
    }
}

CyclicBarrier

package com.yan.util;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() -> System.out.println("召唤神龙!!!"));

        for (int i = 1; i <= 7 ; i++) {
            final int t = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"收集"+t+"颗龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}

Semaphore

package com.yan.util;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {

    public static void main(String[] args) {

        //共有三个车位
        Semaphore semaphore = new Semaphore(3);

        for (int i = 1; i <= 6 ; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"=> 进车库");
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName()+"=> 出车库");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

ReadWriteLock

package com.yan.readwrite;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        for (int i = 1; i <= 6; i++) {
            final int t = i;
            new Thread(() -> {
                myCache.put(t+"",t+"");
            },String.valueOf(i)).start();
        }

        while (Thread.activeCount() > 2){
            Thread.yield();
        }

        for (int i = 1; i <= 6; i++) {
            final int t = i;
            new Thread(() -> {
                myCache.get(t+"");
            },String.valueOf(i)).start();
        }
    }
}

class MyCache {

    private volatile Map<String, Object> map = new HashMap<>();

    private ReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

    public void put(String key, String value) {

        // 写入的时候,只希望同时只有一个线程写
        reentrantReadWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入ok");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            reentrantReadWriteLock.writeLock().unlock();
        }
    }

    public void get(String key) {

        // 取,读,所有人都可以读!
        reentrantReadWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "读取");
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取ok"+o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            reentrantReadWriteLock.readLock().unlock();
        }
    }
}

阻塞队列

四组API

package com.yan.blockingqueue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * 阻塞队列
 */
public class BlockingQueueDemo {

    public static void main(String[] args) throws InterruptedException {


        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        //第一组
//      System.out.println(blockingQueue.add("a"));
//      System.out.println(blockingQueue.add("b"));
//      System.out.println(blockingQueue.add("c"));
//      System.out.println(blockingQueue.element());

//        System.out.println(blockingQueue.add("x"));
//      System.out.println(blockingQueue.remove());
//      System.out.println(blockingQueue.remove());
//      System.out.println(blockingQueue.remove());
//      System.out.println(blockingQueue.remove());
        //第二组
//      System.out.println(blockingQueue.offer("a"));
//      System.out.println(blockingQueue.offer("b"));
//      System.out.println(blockingQueue.offer("c"));
//      System.out.println(blockingQueue.offer("x"));
//      System.out.println(blockingQueue.poll());
//      System.out.println(blockingQueue.poll());
//      System.out.println(blockingQueue.poll());
//      System.out.println(blockingQueue.poll());
        //第三组        
//      blockingQueue.put("a");
//      blockingQueue.put("b");
//      blockingQueue.put("c");
//      blockingQueue.put("x");
//      System.out.println(blockingQueue.take());
//      System.out.println(blockingQueue.take());
//      System.out.println(blockingQueue.take());
//      System.out.println(blockingQueue.take());

        //第四组        
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("a",3L, TimeUnit.SECONDS));

    }
}

线程池

主要特点为:线程复用;控制最大并发数;管理线程。

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的销耗。
第二:提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

三大方法

package com.yan.executor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// Executors 工具类、3大方法
public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
        // ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定的线程池的大小
        // ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的
        try {
            for (int i = 0; i < 100; i++) {
                // 使用线程池来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

七大参数

手动创建一个线程池

package com.yan.executor;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Author: yan
 * @Date: 2020/4/19 15:36
 * @Description: com.yan.executor
 * @version: 1.0
 */
public class ThreadPoolExecutorDemo {

    public static void main(String[] args) {

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,// 核心线程池大小
                5,// 最大核心线程池大小
                3,// 超时释放时间
                TimeUnit.SECONDS,//超时单位
                new LinkedBlockingDeque<>(3),// 阻塞队列
                Executors.defaultThreadFactory(),// 线程工厂:创建线程的,一般不用动
                new ThreadPoolExecutor.AbortPolicy()// 拒绝策略
        );

        for (int i = 1; i <= 5; i++) {
            threadPoolExecutor.execute(() -> {
                System.out.println(Thread.currentThread().getName()+"=> O(∩_∩)O哈哈~");
            });
        }

        threadPoolExecutor.shutdown();
    }
}

四种拒绝策略

new ThreadPoolExecutor.AbortPolicy() // 满了,还有人进来,不处理这个人的,抛出异常

new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!

new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!

new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了尝试去和最早的竞争,也不会抛出异常!

JMM

JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念 并不真实存在,它描述的是一组规则或规范通过规范定制了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式.
JMM关于同步规定:
1.线程解锁前,必须把共享变量的值刷新回主内存
2.线程加锁前,必须读取主内存的最新值到自己的工作内存
3.加锁解锁是同一把锁

Java内存模型中定义了8种操作来完成,虚拟机保证了每种操作都是原子的。

  • lock(锁定):作用于主存的变量,把一个变量标识为一条线程独占状态。
  • unlock(解锁):作用于主存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取):作用于主存变量,把一个变量的值从主存传输到工作内存。
  • load(载入):作用于工作内存变量,把 read 来的值放入工作内存的变量副本中。
  • use(使用):作用于工作内存变量,把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • assign(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
  • store(存储):作用于工作内存变量,把工作内存中一个变量的值传送到主存。
  • write(写入):作用于主存变量,把 store 操作从工作内存中得到的变量的值放入主存的变量中。

JMM对这八种指令的使用,制定了如下规则:

  • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存 (可见)
  • 不允许一个线程将没有assign的数据从工作内存同步回主内存
  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
  • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

Volatile

volatile是Java虚拟机提供的轻量级的同步机制,是基本上遵守了JMM的规范,主要是保证可见性和禁止指令重排,但是它并不保证原子性

保证可见性

package com.yan.jmm;

import java.util.concurrent.TimeUnit;

public class VolatileDemo {
    // 不加 volatile 程序就会死循环!
    // 加 volatile 可以保证可见性
    private volatile static int num = 0;

    public static void main(String[] args) { // main
        new Thread(() -> {
            while (num == 0) {
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}

不保证原子性

package com.yan.jmm;

// volatile 不保证原子性
public class VolatileDemo02 {
    // volatile 不保证原子性
    private volatile static int num = 0;

    public static void add() {
        num++;
    }

    public static void main(String[] args) {
        //理论上num结果应该为 2 万
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while(Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

解决方案 : AtomicInteger

package com.yan.jmm;

// volatile 不保证原子性
public class VolatileDemo02 {

    private volatile static AtomicInteger num = new AtomicInteger();

    public static void add() {
        num.getAndIncrement(); // AtomicInteger + 1 方法, CAS
    }

    public static void main(String[] args) {
        //理论上num结果应该为 2 万
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while(Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

禁止指令重排

CAS

什么是CAS

package com.yan.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {
    // CAS compareAndSet : 比较并交换!
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        // 期望、更新
        // public final boolean compareAndSet(int expect, int update)
        // 如果我期望的值达到了,那么就更新,否则,就不更新, CAS 是CPU的并发原语!
        System.out.println(atomicInteger.compareAndSet(5, 2020)+"\t current"+atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5, 2022)+"\t current"+atomicInteger.get());
    }
}

Unsafe类

  • UnSafe是CAS的核心类 由于Java 方法无法直接访问底层 ,需要通过本地(native)方法来访问,UnSafe相当于一个后面,基于该类可以直接操作特额定的内存数据.UnSafe类在于sun.misc包中,其内部方法操作可以向C的指针一样直接操作内存,因为Java中CAS操作的助兴依赖于UnSafe类的方法.
    注意UnSafe类中所有的方法都是native修饰的,也就是说UnSafe类中的方法都是直接调用操作底层资源执行响应的任务
  • 变量ValueOffset,便是该变量在内存中的偏移地址,因为UnSafe就是根据内存偏移地址获取数据的
  • 变量value和volatile修饰,保证了多线程之间的可见性.

比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!

自旋锁

缺点

  • 循环会耗时
  • 一次性只能保证一个共享变量的原子性
  • ABA问题

ABA问题

package com.yan.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {

    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);

        // ============== 捣乱的线程 ==================
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(2021, 2020));
        System.out.println(atomicInteger.get());
        // ============== 期望的线程 ==================
        System.out.println(atomicInteger.compareAndSet(2020, 6666));
        System.out.println(atomicInteger.get());
    }
}

原子引用

解决ABA问题 => 原子引用

package com.yan.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;

public class CASDemo {

    //AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
    // 正常在业务操作,这里面比较的都是一个个对象
    static AtomicStampedReference<String> atomicStampedReference = new
            AtomicStampedReference<>("1",1);

    public static void main(String[] args) {
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp(); // 获得版本号
            System.out.println("a1=>"+stamp);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet("1", "2",
                    atomicStampedReference.getStamp(),
                    atomicStampedReference.getStamp() + 1);
            System.out.println("a2=>"+atomicStampedReference.getStamp());
            System.out.println(atomicStampedReference.compareAndSet("2", "1",
                    atomicStampedReference.getStamp(),
                    atomicStampedReference.getStamp() + 1));
            System.out.println("a3=>"+atomicStampedReference.getStamp());
        },"a").start();

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp(); // 获得版本号
            System.out.println("b1=>"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicStampedReference.compareAndSet("1", "6",
                    stamp, stamp + 1));
            System.out.println("b2=>"+atomicStampedReference.getStamp());
        },"b").start();
    }
}

施工中...

您的喜欢是作者写作最大的动力!❤️
  • PayPal
  • AliPay
  • WeChatPay
  • QQPay
YAN