JUC 并发

个人学习笔记

生产者和消费者

  • 传统的生产者和消费者的问题
public class Test1 {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.desc();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.desc();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

class Data{
    private int num =0;

    public synchronized void incr() throws InterruptedException {
        while (num!=0){ //放着虚假唤醒
            this.wait();
        }
        System.out.println(Thread.currentThread().getName()+"->"+num);
        num++;

        this.notifyAll();
    }

    public synchronized void desc() throws InterruptedException {
        while(num==0){ // 1
            this.wait();
        }
        System.out.println(Thread.currentThread().getName()+"->"+num);
        num--;

        this.notifyAll();
    }
}

问题:随机的状态
在这里插入图片描述

Condition精准的通知和唤醒线程

public class LockTest {
    public static void main(String[] args) {
        Data2 data2 = new Data2();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                data2.A();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    data2.B();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    data2.C();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
    }
}

class Data2{
    private int num =0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    public void A() {
        lock.lock();
        try{
            while (num!=0){ //0 1
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"->aaa"+num);
            num=1;
            condition2.signal(); //精确通知 condition2执行
        }catch (Exception e){
            e.printStackTrace();
        }finally {

            lock.unlock();
        }


    }
    public void B() throws InterruptedException {
        lock.lock();
        while (num!=1){ //0 1
            condition2.await();
        }

        System.out.println(Thread.currentThread().getName()+"->BBB"+num);
        num=2;
        condition3.signal();
        lock.unlock();
    }

    public void C() throws InterruptedException {
        lock.lock();
        while (num!=2){ //0 1
            condition3.await();
        }

        System.out.println(Thread.currentThread().getName()+"->CCC"+num);
        num=0;
        condition.signal();
        lock.unlock();
    }
}

在这里插入图片描述

锁现象

  • 当call()延迟1秒,先打印 打电话 还是.发短信
/**
 * 当call()延迟1秒,先打印 1.打电话  2.发短信
 */
public class Test2 {
    public static void main(String[] args) {
        Person person = new Person();
        new Thread(()->{
            person.call();
        }).start();

        new Thread(()->{
            person.message();
        }).start();
    }
}

class Person{

    // synchronized 锁的对象是方法的调用者
    // 两个方法用的是同一个锁,谁先拿到谁执行,所以先打印“打电话”
    public synchronized  void call(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("打电话");
    }

    public synchronized void message(){
        System.out.println("发短信");
    }
}
  • 当synchronized锁的是静态方法,先打印哪个
public class Test3 {
    public static void main(String[] args) {
        Person2 person = new Person2();
        new Thread(()->{
            person.call();
        }).start();

        new Thread(()->{
            person.message();
        }).start();
    }
}

class Person2{

    // synchronized 锁的对象是方法的调用者
    // static 静态方法
    // 静态方法,类一加载就有了,静态方法属于Class,所以锁的是Class,全局只有一个Class模板,所以先打印打电话
    public static synchronized  void call(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("打电话");
    }

    public static synchronized void message(){
        System.out.println("发短信");
    }
}

集合类安全问题

常用的辅助类

CountDownLatch 减法计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
    int finalI = i;
    new Thread(()->{
        System.out.println(finalI);
        countDownLatch.countDown();
    }).start();

}
countDownLatch.await(); // 当程序计数器为0之后所有等待线程被释放
System.out.println("jieshu");

运行结果

0
1
2
4
3
5
jieshu
CyclicBarrier 加法计数器
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("结束");
        });

for (int i = 1; i <= 7; i++) {
     int finalI = i;
     new Thread(()->{
         System.out.println(finalI);
         try {
             cyclicBarrier.await();
         } catch (InterruptedException e) {
             e.printStackTrace();
         } catch (BrokenBarrierException e) {
             e.printStackTrace();
         }
     }).start();
 }

运行结果

1
2
3
4
5
6
7
结束
Semaphore 信号计数器

一般用于限流

//线程数量,例子停车,限流
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
    new Thread(()->{
        try {
            semaphore.acquire(); //得到
            System.out.println(Thread.currentThread().getName()+"抢到车位");
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread().getName()+"离开车位");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            semaphore.release(); //释放
        }

    },String.valueOf(i)).start();
}

阻塞队列

四组api

方式抛出异常有返回值,不抛异常阻塞等待超时等待
添加addoffer()put()offer(“1”,2, TimeUnit.SECONDS)
移除removepoll()take()poll(2, TimeUnit.SECONDS)
判断队列首element()peek()

抛出异常:当队列满了或为0了再 添加/读取 会抛异常

 ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
 queue.add(1);
 queue.add(2);
 queue.add(3);
 System.out.println(queue.element());//返回队首
 //IllegalStateException: Queue full
 queue.add(4);

 queue.remove();
 queue.remove();
 queue.remove();
 //NoSuchElementException
 queue.remove();

有返回值,不抛出异常

ArrayBlockingQueue queue = new ArrayBlockingQueue(3);

System.out.println(queue.offer(1));
System.out.println(queue.offer(2));
System.out.println(queue.offer(3));
System.out.println(queue.offer(4));
System.out.println(queue.peek()); // 查看队首

System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());

/** 返回结果
* true
 * true
 * true
 * false
 * 1
 * 2
 * 3
 * null
 */

阻塞 ,等待

ArrayBlockingQueue queue = new ArrayBlockingQueue(3);    
queue.put(1);
queue.put(2);
queue.put(3);
//queue.put(4);  会一直等待阻塞

queue.take();
queue.take();
queue.take();
//queue.take(); 会一直等待阻塞

超时等待 ,超过时长取消等待

ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.offer(2);
queue.offer(3);
queue.offer(4);
queue.offer(1,2, TimeUnit.SECONDS);
System.out.println("jjjjjjjjjjjjjjjj");
queue.poll();
queue.poll();
queue.poll();
queue.poll(2,TimeUnit.SECONDS);
System.out.println("kkkkkkkkkkkkkkkkkk");

同步队列

SynchronousQueue 不存储队列,只能放一个取一个,再放一个

BlockingQueue<String> queue =new SynchronousQueue<String>();

        new Thread(()->{

            try {
                queue.put("a");
                System.out.println(Thread.currentThread().getName()+"插入一条数据a");
                queue.put("b");
                System.out.println(Thread.currentThread().getName()+"插入一条数据b");
                queue.put("c");
                System.out.println(Thread.currentThread().getName()+"插入一条数据c");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+"取出一条数据"+ queue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+"取出一条数据"+ queue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+"取出一条数据"+ queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
      /**执行结果
         * Thread-0插入一条数据a
         * Thread-1取出一条数据a
         * Thread-1取出一条数据b
         * Thread-0插入一条数据b
         * Thread-0插入一条数据c
         * Thread-1取出一条数据c
         */

线程池

四种拒绝策略

ThreadPoolExecutor.CallerRunsPolicy //哪里来得去哪里,哪个方法调用的哪个方法执行
ThreadPoolExecutor.AbortPolicy()  //满了之后不执行,抛出异常
ThreadPoolExecutor.DiscardOldestPolicy //队列满了尝试和最早的竞争
ThreadPoolExecutor.DiscardPolicy //队列满了,丢掉任务,不抛异常

四大函数式接口

Function 函数式接口
在这里插入图片描述

/* Function<String,String> function = new Function<String, String>() {
     @Override
      public String apply(String s) {
          return s;
      }
  };*/
  //简化
  Function<String,String> function = (s) ->{return s;};
  System.out.println(function.apply("abc"));

断言型接口,返回布尔型
在这里插入图片描述

 //判断是否为空
/* Predicate<String> predicate= new Predicate<String>() {
     @Override
     public boolean test(String s) {
         return s.isEmpty();
     }
 };*/
 Predicate<String> predicate=(s)->{return s.isEmpty();};
 System.out.println(predicate.test("jj"));

消费型接口 ,没有返回值
在这里插入图片描述

 /* Consumer<String> consumer = new Consumer<String>() {
    @Override
     public void accept(String s) {
         System.out.println(s);
     }
 };*/
 Consumer<String> consumer = (s)->{
     System.out.println(s);
 };
 consumer.accept("jjj");

供给型函数,只有返回值
在这里插入图片描述

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

异步回调

CompletableFuture

 //没有返回值的 异步回调
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
      try {
          TimeUnit.SECONDS.sleep(5);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println("调用完成");
  });
  completableFuture.get(); //获取阻塞执行结果
  System.out.println("kkkkkkkkkkkkkkkkk");
 
 //有返回值的
 CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{
     System.out.println("supplyAsync");
      return 1024;
 }).whenCompleteAsync((u, t)->{
     System.out.println("u="+u); //正常的返回结果
     System.out.println("t="+t); //错误信息
 }).exceptionally((e)->{
     System.out.println("m="+e.getMessage());
     return 400;
 });
  System.out.println(completableFuture2.get());

JMM

什么事JMM:java内存模型不存在的东西,概念,约定

关于JMM的一些同步约定

  • 线程解锁前必须把变量_立刻_刷回主存
  • 线程加锁前,必须读取主存中的最新值到工作内存中
  • 加锁和解锁是同一把锁

在这里插入图片描述

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

  • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
  • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
  • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
  • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
  • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
  • 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操作之前,必须把此变量同步回主内存
代码实例
 public static int num =0;
 public static void main(String[] args) throws InterruptedException {

     new Thread(()->{
         while (num == 0){

         }
     }).start();
     TimeUnit.SECONDS.sleep(2);
     num = 1;
     System.out.println(num);
 }

程序不知道主内存中的值已经被修改过了,当num=1后,线程仍然不停止,引出volatile

volatile

保证可见性

//当num=1,线程知道主内存中的值已经被修改过了。
public volatile static int num =0;
public static void main(String[] args) throws InterruptedException {
    new Thread(()->{
        while (num == 0){

        }
    }).start();
    TimeUnit.SECONDS.sleep(2);
    num = 1;
    System.out.println(num);
}

不保证原子性

//输出结果 19998
public volatile static int num =0;
public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 20000; i++) {
        new Thread(()->{
                add();
        }).start();
    }
    while (Thread.activeCount()>2){
        Thread.yield();
    }
    System.out.println(num);
}
public static void add(){
    num++;
}

怎么保证原子性

  • 加锁synchronized,lock
  • 使用原子类解决
public  static AtomicInteger num = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 20000; i++) {
        new Thread(()->{
                add();
        }).start();
    }
    while (Thread.activeCount()>2){
        Thread.yield();
    }
    System.out.println(num);
}
public  static void add(){
    num.getAndIncrement();
}

避免指令重排

  • 保证特定操作的执行顺序
  • 可以保证某些变量的内存可见性
    在这里插入图片描述

单例模式

懒汉模式

public class Lazy {
    private Lazy(){
        System.out.println("实例化");
    }

    static volatile Lazy lazy= null;

    public static Lazy getLazy(){
        if(lazy==null){
            synchronized (Lazy.class){
                if(lazy==null){
                    lazy = new Lazy();
                }
            }
        }
        return  lazy;
    }
}

问题:可以通过反射实例化

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
   getLazy();
    Constructor<Lazy> constructor = Lazy.class.getDeclaredConstructor(null);
    constructor.setAccessible(true);
    Lazy lazy = constructor.newInstance();
}

查看newInstance()源码,不可以反射枚举类

if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");

EnumSingle.class

public enum EnumSingle {
    INSTANCE;

    private EnumSingle getInstance(){
        System.out.println("shijili");
        return INSTANCE;
    }
}

运行反射

 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle enumSingle2= EnumSingle.INSTANCE;

        Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        EnumSingle enumSingle = constructor.newInstance();
    }
// 错误信息IllegalArgumentException: Cannot reflectively create enum objects

CAS 理解

AtomicInteger atomicInteger = new AtomicInteger(2020);

//如果期望值达到了,就更新,否则就不更新,CAS 是CPU的并发原语
System.out.println(atomicInteger.compareAndSet(2020,2021)); // true
System.out.println(atomicInteger.get());  //2021
System.out.println(atomicInteger.compareAndSet(2020,2021));  //false
System.out.println(atomicInteger.get());  // 2021

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

缺点

  • 循环会耗时
  • 一次性只能保证一个共享变量的原子性
  • ABA问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值