juc

1.什么是JUC

JAVA Util Concurrent
image-20201214185622208

2.线程和进程

进程是程序的集合,一个进程至少包含一个线程

java默认有两个线程 gc 和main

java真的可以开启线程吗?答案是否定的

        //Thread.start()源码
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
	//调用的是本地方法,java无法直接操作硬件!!
    private native void start0();

并发、并行

  • 并发(多线程同时执行一个资源)

    ​ CPU一核,线程快速交替

  • 并行(多人行走)

    ​ CPU多核,多个线程同时执行;线程池

     //查看最处理器个数
     System.out.println(Runtime.getRuntime().availableProcessors());
    

    并发编程的本质:充分利用cpu的资源

线程有六个状态:new、runnable、blocked、waiting、timed_waiting、terminated

  		//创建
       NEW,
        //运行
       RUNNABLE,

       //阻塞
       BLOCKED,

      //等待
       WAITING,

      //超时等待
       TIMED_WAITING,

       //终止
       TERMINATED;

wait、sleep的区别

  1. 来自不同的类

    wait —>Objec

    sleep---->Thread

    TimeUnit.SECONDS.sleep(1000);
    
  2. 关于锁的释放

    wait:会释放对象锁

    sleep:不会释放对象锁

  3. 使用的范围不同

    wait:必须在同步代码块使用

    sleep:在哪里都可以使用

  4. 是否需要捕获异常

    wait:也会抛异常

    sleep:一定会抛异常:InterruptedException

3.Lock锁

传统锁 synchronized

Lock 接口

image-20201208220723763

image-20201208215730578

1、 Lock lock = new ReentrantLock();
2、 lock.lock();
        try {
           //业务代码
        }catch (Exception e){
            e.printStackTrace();
        }finally {
3、          lock.unlock();
        }

synchronized和Lock的区别

  1. synchronized是关键字,Lock是接口。
  2. synchronized会自动释放锁,Lock必须手动释放锁,否则会产生死锁。
  3. synchronized适合锁住少量的同步代码,Lock适合锁住大量的代码。
  4. synchronized是非公平锁,Lock可以自主设置是公平锁还是非公平锁。
  5. synchronized无法获取锁的状态,Lock可以判断是否获取到了锁。
  6. synchronized 如果线程1获取锁进入阻塞状态,线程2会一直等待,Lock会尝试会尝试再次获取锁

锁是什么?如何判断锁的是谁?

谁调用synchronized所修饰的方法,锁的就是谁!

4.生产者消费者问题

synchroniezd 版本的

private int num=0;
//生产者
public synchronized void product(){
    //如果资源不为0,当前线程停止生产
    if (num!=0){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    num++;
    System.out.println(Thread.currentThread().getName()+"==>"+num);
    //通知其他线程我+1完毕了
    this.notify();
}

//消费者
public synchronized void consumer(){
    //如果资源为0,当前线程停止消费
    if (num==0){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    num--;
    System.out.println(Thread.currentThread().getName()+"==>"+num);
    //通知其他线程我-1完毕了
    this.notify();
}

问题:多个线程会出现 虚假唤醒 :线程被唤醒而没有通知

原因:if 只判断一次

image-20201208231120812

//判断等待、业务、通知
    private int num=0;
    //生产者
    public synchronized void product() throws InterruptedException {
        //如果资源不为0,当前线程停止生产
        while (num!=0){
                this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"==>"+num);
        //通知其他线程我+1完毕了
        this.notifyAll();
    }

    //消费者
    public synchronized void consumer() throws InterruptedException {
        //如果资源为0,当前线程停止消费
        while (num==0){
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"==>"+num);
        //通知其他线程我-1完毕了
        this.notifyAll();
    }

Lock 版本的

image-20201208234647614

private int a=0;
final Lock lock = new ReentrantLock();
final Condition notFull  = lock.newCondition();
final Condition notEmpty = lock.newCondition();

public void increment() throws InterruptedException {
    lock.lock();
    try {
        while (a!=0){
            notFull.await();
        }
        a++;
        System.out.println(Thread.currentThread().getName()+"-->"+a);
        notEmpty.signal();
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        lock.unlock();
    }

}

public void decrement() throws InterruptedException {
    lock.lock();
    try {
        while (a==0){
            notFull.await();
        }
        a--;
        System.out.println(Thread.currentThread().getName()+"-->"+a);
        notEmpty.signal();
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        lock.unlock();
    }

}

synchronized wait notifyAll

Lock await signal

condition的优势 可以 精确线程唤醒某个线程

代码示例

class Aa{
    private int a=1;
    final Lock lock = new ReentrantLock();
    final Condition condition1 = lock.newCondition();
    final Condition condition2 = lock.newCondition();
    final Condition condition3 = lock.newCondition();
//1A 2B 3C
    public void a() throws InterruptedException {
        lock.lock();
        try {
            while (a!=1){
                //等待
                condition1.await();
            }
            a = 2;
            System.out.println(Thread.currentThread().getName()+"AAAAAA");
            //唤醒condition2的锁
            condition2.signal();
        } finally {
            lock.unlock();
        }

    }
    public void b() throws InterruptedException {
        lock.lock();

        try {
            while (a!=2){
                //等待
                condition2.await();
            }
            a = 3;
            System.out.println(Thread.currentThread().getName()+"BBBBBBBB");
             //唤醒condition3的锁
            condition3.signal();
        } finally {
            lock.unlock();
        }
    }
    public void c() throws InterruptedException {
        lock.lock();
        try {
            while (a!=3){
                //等待
                condition3.await();
            }
            a = 1;
            System.out.println(Thread.currentThread().getName()+"CCCCCCCC");
             //唤醒condition1的锁
            condition1.signal();
        } finally {
            lock.unlock();
        }
    }
}

5.8锁现象

如何判断锁的是谁?

synchronized锁的是方法的调用者

 //如果是静态同步方法,synchronized锁的是Class
//如果是普通的同步方法,synchronized锁的是方法的调用者
public static synchronized void call(){
    try {
        TimeUnit.SECONDS.sleep(4);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("打电话");
}

6.集合安全问题

  • ArrayList —> ConcurrentModificationException (并发修改异常)
//解决办法:1.用List list = new Vector<>(); 不推荐,因为它的add方法被synchronized所修饰,效率低
//2. Collections.synchronizedList(list);
//3. JUC 包下的CopyOnWriteArrayList:写入时复制,避免数据被覆盖
  • HashSet —> ConcurrentModificationException (并发修改异常) 解决方法如上
//1. Collections.synchronizedSet(set);
//2. JUC 包下的CopyOnWriteArraySet:写入时复制,避免数据被覆盖

​ HashSet的底层是什么?

public HashSet() {
    map = new HashMap<>();
}

HashSet的add()方法

//add 的本质就是 map.put(key,..)
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
  • HashMap —> ConcurrentModificationException (并发修改异常) 解决方法如上
ConcurrentHashMap<String,String> maps = new ConcurrentHashMap<>();

7.Callable接口

image-20201209123611826
public class CallAble implements Callable<String> {
    public static void main(String[] args) throws Exception {
       CallAble callAble = new CallAble();
        FutureTask futureTask = new FutureTask(callAble);
      	new Thread(futureTask,"a").start();//只打印一次
        new Thread(futureTask,"b").start();
        //get方法可能产生阻塞
        String result = (String) futureTask.get();
        System.out.println(result);
    }

    @Override
    public String call() throws Exception {
        System.out.println(Thread.currentThread().getName());
        return "hello";
    }
}

细节:1 . 有缓存

  1. get()会产生阻塞

8.辅助类

CountDownLatch:倒计时计数器

//等下面的线程都执行完毕后才会执行countDownLatch.await()后面的逻辑
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
    new Thread((
    )->{
        countDownLatch.countDown(); //数量-1
        System.out.println(Thread.currentThread().getName()+"执行完毕");
    },String.valueOf(i)).start();
}
countDownLatch.await(); //等待计数器归零,继续执行下面的逻辑
System.out.println("close door");

CyclicBarrier 加法计数器

CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
    System.out.println("七龙珠手机成功");
});
//下面七个线程执行完毕才会执行上面的线程
 for (int i = 1; i <= 7; i++) {
     final int temp = i;
     new Thread(()->{
         System.out.println(Thread.currentThread().getName()+"执行完毕-"+temp);
         try {
             cyclicBarrier.await();
         } catch (InterruptedException e) {
             e.printStackTrace();
         } catch (BrokenBarrierException e) {
             e.printStackTrace();
         }
     }).start();
 }

作用:等待指定线程执行完毕后,再执行的线程

Semaphore 信号量

image-20201209162853738

public static void main(String[] args) {
    Semaphore semaphore = new Semaphore(3);
    for (int i = 1; i <=6; i++) {
        final int temp=i;
        new Thread(()->{
            try {
                //从该信号量获取许可证,如果已经满了,阻止直到可用。 
                semaphore.acquire();
                System.out.println(temp+"进来了");
                TimeUnit.SECONDS.sleep(1);
                System.out.println(temp+"离开了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //释放许可证,将其返回到信号量。 
                semaphore.release();
            }
        },Thread.currentThread().getName()).start();
    }
}

作用:多个共享资源互斥的使用!并发限流,控制最大线程数!

9.读写锁(ReentrantReadWriteLock)

/*
	读锁  ---》	共享锁		多个线程可以同时占有
	写锁	---》	独占锁     一次只能被一个线程占用
	读 读  可以共存
	读 写  不可以共存
	写 写  不可以共存
*/
public class ReadWriteLockTest {
    private volatile Map<String,Object> map = new HashMap<>();
    //读写锁 更加细粒度的控制
    ReadWriteLock lock = new ReentrantReadWriteLock();
    //写入操作
    public void putMap(String key,Object value){
        try {
            lock.writeLock().lock();
            System.out.println(Thread.currentThread().getName()+"准备写入");
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入ok");
        } finally {
            lock.writeLock().unlock();
        }
    }
    //读取
    public void getMap(String key){
        try {
            lock.readLock();
            System.out.println(Thread.currentThread().getName()+"准备读取");
            System.out.println(Thread.currentThread().getName()+map.get(key));
            System.out.println(Thread.currentThread().getName()+"读取ok");
        } finally {
            lock.readLock().lock();
        }
    }

10.阻塞队列BolckingQueue

image-20201209215955286

*什么时候用到BlockingQueue: 多线程并发处理 线程池

学会使用队列:添加 、移除

NoSuchElementException/IllegalStateException: Queue full

4 组API

方式抛出异常有返回值阻塞等待超时等待
添加add()offer()put()queue.offer(“a”,2, TimeUnit.SECONDS);
移除remove()poll()take()queue.poll(2, TimeUnit.SECONDS);
判断队列首element()peek()--
//抛出异常
public static void arrayBlocking(){
   queue.add("a");
   queue.add("b");
   queue.add("c");
   //IllegalStateException: Queue full
   System.out.println("======================");
    queue.remove();
    queue.remove();
    queue.remove();
    //NoSuchElementException
    queue.remove();
}
/**
 * 不抛异常 有返回值
 * **/
public static void test2(){
    ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
    queue.offer("a");
    queue.offer("b");
    queue.offer("c");
   // System.out.println(queue.offer("d"));
   System.out.println("======================");
    System.out.println(queue.poll());
    System.out.println(queue.poll());
    System.out.println(queue.poll());
    //null
    System.out.println(queue.poll());
}
/**
 * 阻塞等待
 * */
public static void test3() throws InterruptedException {
    ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
    queue.put("a");
    queue.put("b");
    queue.put("c");
    //队列已满,线程阻塞
    queue.put("d");
    System.out.println("======================");
    queue.take();
    queue.take();java
    
    queue.take();
    //队列为空,线程阻塞
    queue.take();

}
/**
 * 超时等待
 * */
public static void test4() throws InterruptedException {
    ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
    queue.offer("a");
    queue.offer("a");
    queue.offer("a");
    queue.offer("a",2, TimeUnit.SECONDS);
    System.out.println("======================");
    queue.poll();
    queue.poll();
    queue.poll();
    queue.poll(2, TimeUnit.SECONDS);

}

11.线程池(重点)

线程池必会:三大方法、七大参数、四大拒绝策略

池化技术

程序的运行 本质:占用系统资源!优化系统资源的使用:池化技术

线程池、连接池、内存池、对象池…

池化技术:事先准备好资源,有人用来我这拿,用完放回池内。

线程池的好处:

  • 降低资源消耗

  • 调好响应速度

  • 方便管理

    线程服用 、控制最大并发数、管理线程

    三大方法

    	ExecutorService executorService = Executors.newSingleThreadExecutor();
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        ExecutorService executorService = Executors.newCachedThreadPool();
        ExecutorService executorService2=Executors.newScheduledThreadPool(10);
        try {
            for (int i = 0; i < 100; i++) {
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
        } finally {
            executorService.shutdown();
        }
    }
    

    七大参数

    public ThreadPoolExecutor(int corePoolSize, //核心线程池大小
    						//如何定义线程池最大线程数?
    						1、cpu密集型:Runtime.getRuntime.availableProcessors();
    						2.IO密集型:判断程序十分消耗IO的线程,设置为它的数量的2倍
                              int maximumPoolSize,//最大线程池数量
                              long keepAliveTime,//超过连接时间没人用,就是释放
                              TimeUnit unit,//超市单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//创建线程 不用动
                              RejectedExecutionHandler handler//拒绝策略) {
    

image-20201210104540061

不要使用Executors创建线程池:

  • FixedThreadPool 和 SingleThreadPool 允许最大的请求队列长度为Integer.Max_value 导致·OOM
  • CachedThreadPool和ScheduledThreadPool 允许最大的线程数量为Integer.Max_value 导致·OOM

四种拒绝策略

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n2K9RsKh-1609254725171)(C:\Users\86199\AppData\Roaming\Typora\typora-user-images\image-20201210105934633.png)]

  • //抛出异常
    new ThreadPoolExecutor.AbortPolicy()
    
  • //哪里来的去哪里	不会抛出异常
    new ThreadPoolExecutor.CallerRunsPolicy());
    
  • //队列满了尝试和第一个线程竞争 不会抛出异常
    new ThreadPoolExecutor.DiscardOldestPolicy());
    
  • //队列满了 丢掉任务 不会抛出异常
    new ThreadPoolExecutor.DiscardPolicy());
    

小结和拓展

最大线程池数量如何设置?

如何定义线程池最大线程数?
1. cpu密集型:Runtime.getRuntime.availableProcessors();
2. IO密集型:判断程序十分消耗IO的线程,设置为它的数量的2

12.四大函数式接口(重点)

函数式接口:只有一个方法的接口

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
//简化编程模型
//foreach(参数):消费式者函数式接口模型
  1. image-20201210115104302
  • Function
  1. Function function = new Function<String,String>() {
        @Override
        public String apply(String o) {
            return o;
        }
    };
    System.out.println(function.apply("tt"));
    Function f = (str)->{return str;};
    
  • Predicate 断定式接口

            Predicate<String> predicate = new Predicate<String>() {
                @Override
                public boolean test(String s) {
                    return s.isEmpty();
                }
            };
            System.out.println(predicate.test("ss"));
            Predicate<String> predicates = s -> {return s.isEmpty();};
            System.out.println(predicates.test(""));
    
  • Consumer 消费式接口:只有参数,没有返回值

    public static void main(String[] args) {
            /*Consumer<String> consumer = new Consumer<String>() {
                @Override
                public void accept(String s) {
                    System.out.println(s);
                }
            };
            consumer.accept("ss");*/
            Consumer<String> consumers = s -> {
                System.out.println("s");
            };
        }
    
  • Supplier 生产式接口 没有参数,只有返回值

     /*Supplier<String> stringSupplier = new Supplier<String>() {
                @Override
                public String get() {
                    return "ss";
                }
            };
            System.out.println(stringSupplier.get());*/
            Supplier<String> stringSuppliers = ()->{
                return "sss";
            };
            System.out.println(stringSuppliers.get());
    

13.流式计算 Stream

  User u1 = new User(1,"a",20);
        User u2 = new User(2,"b",21);
        User u3 = new User(3,"c",23);
        User u4 = new User(4,"d",24);
        User u5 = new User(5,"e",25);
        List<User> users = Arrays.asList(u1,u2,u3,u4,u5);

       users.stream()
               .filter(user -> {return user.getId()%2==0;})
               .filter(user -> {return user.getAge()>20;})
               .map(user -> { return user.getName().toUpperCase();})
               .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
               .limit(1).forEach(System.out::println);

14.ForkJoin

什么是ForkJoin

ForkJoin在jdk1.7中,并行执行任务,提高效率。大数据量!

image-20201210160703919

ForkJoin特点:工作窃取

这里面维护的都是双端队列

image-20201210161112055

image-20201210163641594

ForkJoin使用步骤:

  1. ForkJoinPool forkJoinPool = new ForkJoinPool();

  2. long res = forkJoinPool.invoke(RecursiveTask task);

  3. 计算类继承 RecursiveTask

/**
 * 1.传统算法  2. ForkJoin  3,Stream流
 */
public class Demon extends RecursiveTask<Long> {
    private long start;
    private long end;

    public Demon(long start,long end) {
        this.start = start;
        this.end=end;
    }

    //临界点 超过该值将任务拆分
    private static long temp = 10000l;
    public static void main(String[] args) {
       // test1(); //683
        Long before = System.currentTimeMillis();
        Demon demon = new Demon(1,10_0000_0000);
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        long res = forkJoinPool.invoke(demon);
        Long end = System.currentTimeMillis();
        System.out.println("sum="+res+"\t"+(end-before)); //331*/
        //test3();//177
    }
    public static void test1(){
        long sum = 0;
        Long before = System.currentTimeMillis();
        for (long i = 1; i <= 10_0000_0000; i++) {
            sum +=i;
        }
        Long end = System.currentTimeMillis();
        System.out.println("sum="+sum+"\t"+(end-before));
    }

    /**如何使用forkjoin?
     * 1.ForkJoinPool forkJoin = newForkJoinPool();
     * 2.forkJoin.execute(ForkJoinTask<?> task)
     * 3.创建ForkJoinTask
     */
    @SneakyThrows
    @Override
    protected Long compute() {
        //超过该值 分支合并
        if ((end-start)>temp){
           long middle = (start+end)/2;
            Demon task1 = new Demon(start,middle);
            task1.fork();
            Demon task2 = new Demon(middle+1,end);
            task2.fork();
            return task1.join()+task2.join();
        }else {
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum +=i;
            }
            return sum;
        }
    }

15.异步回调

 //没有返回值的异步回调
 CompletableFuture completableFuture = CompletableFuture.runAsync(()->{
    try {
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
 System.out.println("111111111");
 completableFuture.get();//该方法会阻塞 直到上面的线程执行完毕

16.理解volatile

请你谈谈对volatile的理解

volatile是java中的关键字,是虚拟机提供的轻量级的同步机制。

  1. 保证可见性

    private volatile static int num=0;
    
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            while (num==0){
               // System.out.println("----"+num);
            }
        }).start();
        TimeUnit.SECONDS.sleep(3);
        num=1;
        System.out.println("===="+num);
    }
    
  2. 不保证原子性

    原子性:不可分割,要么全部执行,要么全部不执行。

    解决办法:synchronized,但是不建议,因为volatile是轻量级synchronized

    public static void test(){
    //num++不是原子性操作
        num++;
    }
    

    使用原子类解决该问题:
    image-20201211134203237

static volatile AtomicInteger atomicInteger = new AtomicInteger();
public static void test(){
   atomicInteger.getAndIncrement();
}
  1. 禁止指令重排

    你写的程序并不是按照你写的顺序执行的

    源代码—>编译器优化重排—>指令并行也可能重排—>内存系统也可能会重排—>执行

volatile可以避免指令重排

内存屏障,CPU指令,作用:

  1. 确保特定的操作执行顺序。

  2. 可以保证某些变量的内存可见性。

    17.JMM

什么是JMM

JAVA内存模型,是一种约定,并不存在

  1. 线程解锁前必须把共享变量立即刷新到主内存。
  2. 线程加锁前线程必须把主存中的罪行变量读取到自己的工作内存
  3. 加锁和解锁必须是一把锁

8种操作

JMM

这8种操作每一对都不能分割。

javap -c 字节码文件

jad -sjava 字节码文件

18.彻底玩转单例模式

饿汉式单例

public class Hunger {
    private byte[]b = new byte[1024*1024];
    private byte[]b = new byte[1024*1024];
    private byte[]b = new byte[1024*1024];
    private static final Hunger intance = new Hunger();
    private Hunger(){};
    public Hunger getInstance(){
        return intance;
    }
}

问题: 造成内存浪费

解决办法:懒汉式单例

懒汉式单例(DCL)

public class Lazy {
    private volatile static Lazy lazy;
    private Lazy(){
           synchronized (Lazy.class){
               if (lazy!=null){
                   throw new RuntimeException("不要试图通过反射破坏我");
               }
        }
    }
    public static Lazy getInstance(){
        if (lazy==null){
            synchronized (Lazy.class){
                if (lazy==null){
                    lazy = new Lazy();
                }
            }
        }
        return lazy;
    }
     public static void main(String[] args) throws Exception {
        //反射破坏单例模式
       Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        //使private 失效
        declaredConstructor.setAccessible(true);

        Lazy lazy1 = declaredConstructor.newInstance();
        Lazy lazy11 = declaredConstructor.newInstance();
        Lazy lazy2 = Lazy.getInstance();
        System.out.println(lazy1.hashCode());
        System.out.println(lazy11.hashCode());
        //System.out.println(lazy1==lazy2); false
    }
}

问题:1、普通的懒汉式单例在多线程的情况下不安全,会生成多个instance

​ 2、由于 lazy = new Lazy();可能会发生指令重排,因此用volatile修饰instance

解决: 1、 双重检验锁

​ 2、volatile修饰instance

单例不安全,因为有反射!

枚举的单例模式是不能被反射的!!!

javap -c A.class

jad -sjava A.class

public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }

    public static void main(String[] args) throws Exception {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        //试图用反射破坏
        Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor( String.class,int.class);
        //抛出异常
        EnumSingle enumSingle = constructor.newInstance();
        System.out.println(enumSingle==instance1);

    }
}

image-20201214114836968

19、深入理解CAS

什么是CAS ? CompareAndSwap

private static AtomicInteger atomicInteger = new AtomicInteger(200);

public static void main(String[] args) {
    //如果是200 则更新为3
    boolean f = atomicInteger.compareAndSet(200,3);
    System.out.println(f);//true
    System.out.println(atomicInteger);//3
    atomicInteger.compareAndSet(200,4);
    System.out.println(f);
    System.out.println(atomicInteger);
}
//如果是期望的值,则更新为update
public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

Unsafe类

image-20201214124050827

image-20201214124627428

内存操作,效率高

CAS:比较工作内存和主内存的值,如果这个值是期望的,则执行操作,如果不是一直循环!

ABA 问题:狸猫换太子

20.原子引用

AtomicInteger integer = new AtomicInteger(100);

//捣乱的线程
 System.out.println(integer.compareAndSet(100,101));
 System.out.println(integer.get());
 System.out.println(integer.compareAndSet(101,100));
 System.out.println(integer.get());
 //期望的线程
 System.out.println(integer.compareAndSet(100,101));
 System.out.println(integer.get());

如何解决?原子引用

解决ABA问题:引入原子引用,类似乐观锁。带版本号的原子操作

public static void main(String[] args) throws InterruptedException {
    /**
     * 解决ABA问题的办法就是原子引用,类似乐观锁,通过加版本号 让A线程操作完成后让B线程知道,
     */
    AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(100, 1);
    new Thread(()->{
        System.out.println(reference.compareAndSet(100, 101, reference.getStamp(), reference.getStamp() + 1));
        //获取版本号+reference的值
        System.out.println(reference.getStamp()+"\t"+reference.getReference());
    },"A").start();

    new Thread(()->{
        System.out.println(reference.compareAndSet(101, 102, reference.getStamp(), reference.getStamp() + 1));
        System.out.println(reference.getStamp()+"\t"+reference.getReference());
    },"B").start();
}

21.各种锁的理解

21.1 公平锁、非公平锁

公平锁:不能插队,线程先来先执行

非公平锁:可以插队,3s 3h

Lock、Synchronized 默认都是非公平锁。

public ReentrantLock() {
    sync = new NonfairSync();
}

21.2 可重入锁(递归锁)

synchronized版

/**
 * synchronized版本的 可重入锁 A线程获取A锁后自动获取B锁,然后释放
 */
public class ReenLockTest {
    public static void main(String[] args) {
        new Thread(()->{
            call();
        },"A").start();

        new Thread(()->{
            call();
        },"B").start();
    }
    public static synchronized void call(){
        System.out.println(Thread.currentThread().getName()+"call");
        message();
    }
    public static synchronized void message(){
        System.out.println(Thread.currentThread().getName()+"message");
    }

Lock版本

/**
 * Lock版本的可重入锁
 */
public class ReenLockTest2 {
    public static void main(String[] args) {
        new Thread(()->{
            call();
        },"A").start();

        new Thread(()->{
            call();
        },"B").start();
    }
    static Lock lock = new ReentrantLock();
    public static  void call(){
       //加锁和解锁必须配对,否则会死锁
        lock.lock();
        //lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"call");
            message();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public static  void message(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"message");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

21.3 自旋锁

/**
 * 通过CAS实现
 */
public class ZiXuan {
    static AtomicReference reference = new AtomicReference<Thread>();
    static Thread thread = Thread.currentThread();
    //加锁
    public static void mylock(){
        System.out.println(Thread.currentThread().getName()+"加锁");
        while (!reference.compareAndSet(null,thread)){

        }
    }
    //解锁
    public static void myunlock(){
        System.out.println(Thread.currentThread().getName()+"解锁");
        reference.compareAndSet(thread,null);
    }

    public static void main(String[] args) {
        new Thread(()->{
            try {
                mylock();
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                myunlock();
            }
        },"A").start();
    }
}

21.4 死锁

image-20201214174715496

产生死锁的四个必要条件
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 占有且等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不可强行占有:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

解决问题:

jps -l 查看进程

jstack 进程号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值