JUC并发编程

 

什么是JUC

JUC :是Java.util下的工具包、包、分类

线程和进程

进程: 一个程序,程序的集合,一个进程往往可以包含多个线程,至少包含一个

线程:进程的一种实体,一个进程至少有一个线程,是CPU、调度分配资源的最小单位

Java开启不了线程

并发、并行

并发:多线程操作同一个资源

并行:多个线程可以同时执行

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

线程有几个状态

  1. 新生(NEW)

  2. 运行(RUNNABLE)

  3. 阻塞(BLOCKED)

  4. 等待(WAITNG)

  5. 超时等待(TIMED_WAITING)

  6. 终止(TERMINATED)

wait/sleep的区别?

  1. 来自不同的类

    wait => Object

    sleep => Thread

  2. 关于锁的释放

    wait会释放锁,sleep不会释放锁

  3. 使用的范围不同

    wait必须在同步代码块中

    sleep可以在任何地方

Synchronized和Lock的区别?

  1. synchronized 内置关键字,Lock是一个Java类

  2. synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁

  3. synchronized 会自动释放锁,Lock必须要手动释放

  4. synchronized 可重入锁,不可以中断的,非公平的;Lock,可重入锁,可以判断锁,非公平

  5. synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码

 

生产者和消费者问题

package com.zhong.pc;

/**
* 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num + 1
* B num - 1
*/
public class A {

   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.increment();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      },"B").start();
       new Thread(() ->{
           for (int i = 0; i < 10; i++) {
               try {
                   data.decrement();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      },"C").start();
       new Thread(() ->{
           for (int i = 0; i < 10; i++) {
               try {
                   data.decrement();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      },"D").start();
  }
}

//判断等待,业务,通知
class Data{//数字 资源类

   private int number = 0;

   //+1
   public synchronized void increment() throws InterruptedException {
       //线程不等于0 进行while判断
       if (number != 0){
           //等待
           this.wait();
      }
       //线程进行while循环结束,自增
       number++;
       System.out.println(Thread.currentThread().getName()+"=>"+number);
       //通知其他线程,执行完毕
       this.notifyAll();
  }

   //-1
   public synchronized void decrement() throws InterruptedExceptifion {
      if (number == 0){
           //等待
           this.wait();
      }
       number--;
       System.out.println(Thread.currentThread().getName()+"=>"+number);
       //通知其他线程,执行完成
       this.notifyAll();
  }
}

注意点:为了防止虚假唤醒问题,if 改为 while 判断

package com.zhong.pc;

/**
* 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num + 1
* B num - 1
*/
public class A {

   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.increment();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      },"B").start();
       new Thread(() ->{
           for (int i = 0; i < 10; i++) {
               try {
                   data.decrement();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      },"C").start();
       new Thread(() ->{
           for (int i = 0; i < 10; i++) {
               try {
                   data.decrement();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      },"D").start();
  }
}

//判断等待,业务,通知
class Data{//数字 资源类

   private int number = 0;

   //+1
   public synchronized void increment() throws InterruptedException {
       //线程不等于0 进行while判断
       while (number != 0){
           //等待
           this.wait();
      }
       //线程进行while循环结束,自增
       number++;
       System.out.println(Thread.currentThread().getName()+"=>"+number);
       //通知其他线程,执行完毕
       this.notifyAll();
  }

   //-1
   public synchronized void decrement() throws InterruptedException {
       while (number == 0){
           //等待
           this.wait();
      }
       number--;
       System.out.println(Thread.currentThread().getName()+"=>"+number);
       //通知其他线程,执行完成
       this.notifyAll();
  }
}

JUC版的生产者和消费者问题

  1. 通过Lock找到Condition

  2. Condition精准的通知和唤醒线程

package com.zhong.pc;

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

/**
* A 执行完调用 B,B执行完调用C,C执行完调用A
*/
public class C {
   public static void main(String[] args) {

       Data3 data3 = new Data3();

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


class Data3{

   private Lock lock = new ReentrantLock();
   private Condition condition1 = lock.newCondition();
   private Condition condition2 = lock.newCondition();
   private Condition condition3 = lock.newCondition();
   private int number = 1;

   public void printA(){
       lock.lock();
       try {
           while (number != 1){
               //等待
               condition1.await();
          }
           System.out.println(Thread.currentThread().getName()+"->"+number);
           //唤醒,唤醒指定的人,B
           number = 2;
           condition2.signal();
      } catch (InterruptedException e) {
           e.printStackTrace();
      }finally {
           lock.unlock();
      }
  }

   public void printB(){
       //上锁
       lock.lock();
       try {
           while (number != 2){
               condition2.await();
          }
           System.out.println(Thread.currentThread().getName()+"->"+number);
           number = 3;
           //释放锁
           condition3.signal();
      } catch (InterruptedException e) {
           e.printStackTrace();
      }finally {
           //关闭资源
           lock.unlock();
      }
  }
   public void printC(){
       lock.lock();
       try {
           //业务,判断 -> 执行-> 通知
           while (number != 3){
               condition3.await();
          }
           System.out.println(Thread.currentThread().getName()+"->"+number);
           //唤醒,唤醒指定的人
           number = 1;
           condition1.signal();
      } catch (InterruptedException e) {
           e.printStackTrace();
      }finally {
           lock.unlock();
      }
  }
}

八锁现象

八锁就是关于锁的8个问题

package com.zhong.lock8;


import java.util.concurrent.TimeUnit;

/**
* 8锁,就是关于锁的8个问题
* 1、标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
* 1、sendSms延迟4秒,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
*/
public class Test1 {
   public static void main(String[] args) {

       phone phone = new phone();

       //锁的存在
       new Thread(() -> {
           phone.sendSms();

      },"A").start();


       //捕获
       try {
           TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }

       new Thread(() -> {
           phone.call();
      },"B").start();
  }
}

class phone{

   //synchronized 锁的对象是方法的调用者!
   //两个方法用的是同一个锁,谁先拿到谁就执行
   public synchronized void sendSms(){
       try {
           TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println("发短信");
  }

   public synchronized void call(){
       System.out.println("打电话");
  }
}

Callable

  1. 可以有返回值

  2. 可以抛出异常

  3. 方法不同,run()/call()

package com.zhong.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
* 1、探究原理
* 2、觉自己会用
*/
public class CallableTest {
   public static void main(String[] args) throws ExecutionException, InterruptedException {
       // new Thread(new Runnable()).start();
       // new Thread(new FutureTask<V>()).start();
       // new Thread(new FutureTask<V>( Callable )).start();

       new Thread().start();//怎么启动Callable

       MyThread thread = new MyThread();
       FutureTask futureTask = new FutureTask(thread);//适配类

       new Thread(futureTask,"A").start();
       new Thread(futureTask,"B").start();//结果会被缓存,效率高

       Integer o = (Integer) futureTask.get();//这个get方法可能会产生阻塞!把他放到最后
       //或者使用异步通信来处理
       System.out.println(o);
  }
}

class MyThread implements Callable<Integer>{


   @Override
   public Integer call() throws Exception {
       System.out.println("call()");//会打印几个call
       //耗时操作
       return 1024;
  }
}

常用的辅助类

  1. CountDownLatch

    允许一个或多个线程等待查到在其他线程中执行的一组操作完成的 同步辅助

    package com.zhong.add;

    import java.util.concurrent.CountDownLatch;

    //计数器
    public class CountDownLatchDemo {

       public static void main(String[] args) throws InterruptedException {
           //总数是6,必须要执行任务的时候,再使用!
           CountDownLatch countDownLatch = new CountDownLatch(6);

           for (int i = 1; i <= 6; i++) {
               new Thread(()-> {
                   System.out.println(Thread.currentThread().getName()+"Go out");
                   countDownLatch.countDown();//数量-1
              },String.valueOf(i)).start();
          }
           countDownLatch.await();//等待计算器归零,然后再向下执行

           System.out.println("Close Door");
      }
    }
  2. CyclicBarrier

    允许一组线程全部等待彼此达到共同屏障点的同步辅助

    package com.zhong.add;

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

    public class CynlicBarrierDemo {
       /**
        * 集齐7颗龙珠召唤神龙
        */
       public static void main(String[] args) {
           // 召唤龙珠的线程
           CyclicBarrier cyclicBarrier = new CyclicBarrier(8,()->{
               System.out.println("召唤神龙成功!");
          });

           for (int i = 1; i <= 7; i++) {
               final int temp = i;
               //lambda能操作到i吗
               new Thread(()->{
                   System.out.println(Thread.currentThread().getName()+"收集"+temp+"个龙珠");
                   try {
                       cyclicBarrier.await();//等待
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  } catch (BrokenBarrierException e) {
                       e.printStackTrace();
                  }
              }).start();
          }
      }
    }
  3. Semaphore

    一个计数信号量,在概念上,信号量维持一组许可证。如果有必要,每个qcquire()都会阻塞,直到许可证可用,然后才能使用它

package com.zhong.add;

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(()->{
               //acquire()得到
               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();//release()释放
              }
          },String.valueOf(i)).start();
      }
  }
}

读写锁

ReadWriteLock:读可以被多线程同时读写的时候只能有一个线程去写

package com.zhong.rw;

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

/**
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁) 多个线程可以同时占有
* ReadWriteLock
* 读-读 可以共存!
* 读-写 不能共存!
* 写-写 不能共存!
*/
public class ReadWriteLockDemo {

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

       //写入
       for (int i = 1; i <= 5; i++) {
           final int temp = i;
           new Thread(()->{
               myCache.put(temp+"",temp+"");
          },String.valueOf(i)).start();
      }

       //读取
       for (int i = 1; i <= 5; i++) {
           final int temp = i;
           new Thread(()->{
               myCache.get(temp+"");
          },String.valueOf(i)).start();
      }
  }
}

//加锁的
class MyCacheLock{

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

   //读写锁:更加细粒度的控制
   private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
   private Lock lock = new ReentrantLock();

   //存,写入的时候,只希望同时只有一个线程写
   public void put(String key,Object value){
       readWriteLock.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 {
           readWriteLock.writeLock().unlock();
      }
  }

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


//自定义缓存
class MyCache{

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

   //存,写
   public void put(String key,Object value){
       System.out.println(Thread.currentThread().getName()+"写入"+key);
       map.put(key,value);
       System.out.println(Thread.currentThread().getName()+"写入ok");
  }

   //取,读
   public void get(String key){
       System.out.println(Thread.currentThread().getName()+"读取"+key);
       Object o = map.get(key);
       System.out.println(Thread.currentThread().getName()+"读取ok");
  }
}

阻塞队列

什么情况下使用 阻塞队列:多线程并发处理,线程池

四组API

方式抛出异常有返回值,不抛出异常阻塞等待超时等待
添加 add offer() put() offer(..)
移除 remove poll() take() poll(,)
检测队首元素 element peek - -

线程池

线程池:三大方法、7大参数、4种拒绝策略

池化技术:事先准备好的一些资源,有人要用,就来我这里拿,用完之后还给我

程序的运行本质:占用斜体的资源!优化资源的使用!=> 池化技术

线程池的好处:

  1. 降低资源的消耗

  2. 提高响应的速度

  3. 方便管理

  4. 线程复用、可以控制最大并发数、管理线程

 

四大函数式接口

lambda表达式、链式编程、函数式接口、stream流式计算

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

断定型接口:有一个输入参数,返回值只能是布尔值!

JMM

Volatile的理解:Volatile是Java虚拟机提供轻量级的同步机制

  1. 保证可见性

  2. 不保证原子性

  3. 禁止指令重排

什么是JMM

JMM:Java内存模型,不存在的东西,概念!约定!

JMM的同步约定:

  1. 线程解锁前,必须把共享变量立刻刷回主存

  2. 线程加锁前,必须读取主存的最新值到工作内存中!

  3. 加锁和解锁是同一把锁

内存交互操作:lock(锁定)、unlock(解锁)、read(读取)、load(载入)、use(使用)、assign(赋值)、store(存储)、write(写入)

JMM对八种指令的规则:

  1. 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须 write 不允许线程丢弃他近的assign操作,即工作变量的数据改变了之后,必须告知主存

  2. 不允许一个线程将没有assign的数据从工作内存同步回主内存 一个新的变量必须在主内存中诞生,

  3. 不允许工作内存直接使用一个未被初始化的变量。就是怼变量 实施use、store操作之前,必须经过assign和load操作

  4. 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解 锁

  5. 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前, 必须重新load或assign操作初始化变量的值

  6. 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量

  7. 对一个变量进行unlock操作之前,必须把此变量同步回主内存

Volatile

  1. 保证可见性

  2. 不保证原子性

    原子性:不可分割,要么同时成功,要么同时失败

  3. volatile可以避免指令重排

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

处理器在进行指令重排的时候,考虑:数据之间的依赖性

volatile避免指令重排:内存屏障。CPU指令。作用:

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

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

各种锁的理解

  1. 公平锁、非公平锁

    公平锁:非常公平,不能够插队,必须先来后到

    非公平锁:非常不公平,可以插队(默认)

  2. 可重入锁

    可重入锁(递归锁)

  3. 自旋锁

  4. 死锁

posted on 2020-11-19 15:36  白糖℃  阅读(114)  评论(0编辑  收藏  举报