多线程进阶=>JUC编程

多线程进阶-->JUC并发编程

1.什么是JUC

源码+官方文档

java.util工具包,包,分类

业务:普通的线程代码 Thread

Runnable没有返回值,效率相比Callable相对较低

2.线程和进程

线程,进程,如果不能使用一句话说出来的技术,不扎实

进程:一个程序,QQ.exe Music.exe程序的集合;

一个进程可以包含多个线程,至少包含一个

Java默认有几个线程?2个 main ,GC

线程:开了一个进程Typora,写字自动保存(线程负责的)

对于java而言:Thread,Runnable,Callable

Java真的可以开启线程吗?开不了

public synchronized void start() {
       /**
        * This method is not invoked for the main method thread or "system"
        * group threads created/set up by the VM. Any new functionality added
        * to this method in the future may have to also be added to the VM.
        *
        * A zero status value corresponds to state "NEW".
        */
       if (threadStatus != 0)
           throw new IllegalThreadStateException();

       /* Notify the group that this thread is about to be started
        * so that it can be added to the group's list of threads
        * and the group's unstarted count can be decremented. */
       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 */
          }
      }
  }
//本地方方法,底层的 C++,Java无法直接操作硬件
private native void start0();

 

并发,并行

并发编程:并发,并行

并发(多线程同时操作同一资源)

。CPU一核,模拟出来多条线程,天下武功,为快不破,快速交替

并行(多个人一起行走)

。CPU多核,多个线程可以同时执行;

package com.yu.demo01;

public class Test1 {
   public static void main(String[] args) {
       //获取cpu的核数
       //cpu密集型,IO密集型
       System.out.println(Runtime.getRuntime().availableProcessors());
  }
}

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

所有公司都很看重

企业,挣钱-->提高效率,裁员,找一个厉害的人顶替三个不怎么样的人

人员(减) 技术成本(高)

 

线程有几个状态

public enum State {
       //新生
       NEW,

       //运行
       RUNNABLE,

      //阻塞
       BLOCKED,

     //等待 死死的等
       WAITING,

       //超时等待
       TIMED_WAITING,

       //终止
       TERMINATED;
  }

 

wait/sleep 区别

1.来自不同的类

wait-->Object

sleep-->Thread

2.关于所锁的释放

wait会释放锁,sleep睡觉了,抱着锁睡觉的,不会释放

3.使用的范围是不同的

wait必须在同步代码块中

sleep可以在任何地方睡

4.是否需要捕获异常

wait不需要捕获异常

sleep必须要捕获异常

3.Lock锁(重点)

传统的synchronized

package com.yu.demo01;
/*
* 真正的多线程并发公司中的并发,降低耦合性
* 线程就是一个单独的一个资源类,没有任何附属的操作
* 1.属性 方法
* */
public class SaleTicketDemo01 {
   public static void main(String[] args) {
   //并发:多线程操作同一资源类。把资源类丢入线程
       Ticket ticket = new Ticket();

       //@FunctionalInterface 函数式接口 jdk1.8   lambda表达式(参数)->{代码}
       new Thread(()->{
           for (int i = 0; i < 40; i++) {
               ticket.sale();
          }
      },"A").start();
       new Thread(()->{
           for (int i = 0; i < 40; i++) {
               ticket.sale();
          }
      },"B").start();
       new Thread(()->{
           for (int i = 0; i < 40; i++) {
               ticket.sale();
          }
      },"C").start();
  }
}
//资源类
class Ticket{
   //属性 方方法
   private  int number = 30;

   //买票的方式
   //synchronized 本质:队列,锁
   public synchronized void  sale(){
       if (number>0){
           System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:"+number);
      }
  }
   //对象 class
}

 

Lock接口

公平锁:十分公平,可以先来后到

非公平锁:十分不公平:可以插队(默认

package com.yu.demo01;

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

public class SaleTicketDemo02 {
   public static void main(String[] args) {
       //并发:多线程操作同一资源类。把资源类丢入线程
       Ticket2 ticket = new Ticket2();

       //@FunctionalInterface 函数式接口 jdk1.8   lambda表达式(参数)->{代码}
       new Thread(()->{ for (int i = 0; i < 40; i++) ticket.sale(); },"A").start();
       new Thread(()->{ for (int i = 0; i < 40; i++) ticket.sale(); },"B").start();
       new Thread(()->{ for (int i = 0; i < 40; i++) ticket.sale(); },"C").start();

  }
}
   //lock三部曲
   //1. new ReentrantLock();
   //2.lock.lock();//加锁
   //3. finally--> lock.unlock(); //解锁
   class Ticket2 {
       //属性 方法
       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(); //解锁
          }
      }
       //锁的是 对象 class
  }

Synchronized和Lock区别

1.Synchronized 内置的java关键字,Lock是一个java类

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

3.Synchronized 会自动释放锁,Lock必须要手动释放锁!如果不释放,死锁

4.Synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等),Lock就不一定会等待下去

5.Synchronized 可重入锁,不可以中断的,非公平;Lock 可重入锁,可以判断锁,非公平(可以自己设置)

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

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

4.生产者和消费者问题

面试:单例模式,排序算法,生产者和消费者问题,死锁

生产者和消费者问题 Synchronized版

package com.yu.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.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);
       //通知其他线程,我+1完毕了
       this.notifyAll();
  }

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

问题存在 A B C D四个线程!虚假唤醒

if 改成 while判断

package com.yu.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.decrement();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      },"B").start();

       new Thread(()->{
           for (int i = 0; i < 10; i++) {
               try {
                   data.increment();
              } 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 {
       while (number != 0){//0
           //等待
           this.wait();
      }
       number++;
       System.out.println(Thread.currentThread().getName()+"=>"+number);
       //通知其他线程,我+1完毕了
       this.notifyAll();
  }

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

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

![(E:\kuangshnegspeakjava\image\屏幕截图 2021-05-23 165358.png)

代码实现

package com.yu.pc;

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

public class B {
   public static void main(String[] args) {
       Data2 data = new Data2();

       new Thread(()->{
           for (int i = 0; i < 10; i++) {
               try {
                   data.increment();
              } catch (Exception e) {
                   e.printStackTrace();
              }
          }
      },"A").start();

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

       new Thread(()->{
           for (int i = 0; i < 10; i++) {
               try {
                   data.increment();
              } catch (Exception 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 Data2 {//数字 资源类
       private int number = 0;

       Lock lock = new ReentrantLock();
       Condition condition = lock.newCondition();
       //condition.await();//等待
       //condition.signalAll();//唤醒全部
       //+1
       public  void increment() {
           lock.lock();
           try {
               while (number != 0) {//0
                   //等待
                   condition.await();
              }
               number++;
               System.out.println(Thread.currentThread().getName() + "=>" + number);
               //通知其他线程,我+1完毕了
               condition.signalAll();
          }catch (Exception e){
               e.printStackTrace();
          }finally {
               lock.unlock();
          }
      }

       //-1
       public  void decrement() throws InterruptedException {
           lock.lock();
           try {
               while (number == 0) {//1
                   //等待
                   condition.await();
              }
               number--;
               System.out.println(Thread.currentThread().getName() + "=>" + number);
               //通知其他线程,我-1完毕了
               condition.signalAll();
          }catch (Exception e){
               e.printStackTrace();
          }finally {
               lock.unlock();
          }

      }
  }

任何一个新的技术,绝对不是仅仅只是覆盖了原来的技术,优势和补充

Condition 精准的通知和唤醒线程

   

代码实现

package com.yu.pc;

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

/**
* @author 狂神说java
* A执行完调用B,B执行完调用C,C执行完调用A
*/

public class C {
   public static void main(String[] args) {
       Data3 data = new Data3();

       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 Data3{//资源类 Lock
   private Lock lock = new ReentrantLock();
   private Condition condition1 = lock.newCondition();
   private Condition condition2 = lock.newCondition();
   private Condition condition3 = lock.newCondition();
   private int number = 1; //1A 2B 3C
   public void printA(){
       lock.lock();
       try {
           //业务,判断->执行->通知
           while (number != 1){
               //等待
               condition1.await();
          }
           System.out.println(Thread.currentThread().getName()+"=>AAAAA");
           number = 2;
           condition2.signal();
      } catch (Exception e) {
           e.printStackTrace();
      }finally {
           lock.unlock();
      }
  }
   public void printB(){
       lock.lock();
       try {
           //业务,判断->执行->通知
           while (number != 2){
               //等待
               condition2.await();
          }
           System.out.println(Thread.currentThread().getName()+"=>BBBBB");
           number = 3;
           condition3.signal();
      } catch (Exception e) {
           e.printStackTrace();
      }finally {
           lock.unlock();
      }
  }
   public void printC(){
       lock.lock();
       try {
           //业务,判断->执行->通知
           while (number != 3){
               //等待
               condition3.await();
          }
           System.out.println(Thread.currentThread().getName()+"=>CCCCC");
           number = 1;
           condition1.signal();
      } catch (Exception e) {
           e.printStackTrace();
      }finally {
           lock.unlock();
      }
  }
}

5.8锁现象

如何判断锁的是谁!永远的知道什么是锁,锁到底锁的谁!

对象 Class

package com.yu.lock8;

import java.util.concurrent.TimeUnit;

/*
* 8锁,就是关于锁额8个问题
* 1.标准情况下,两个线程先打印 发短信还是打电话 1发短信 2打电话
* 2.sendSms延迟4s,两个线程先打印 发短信还是打电话
* */
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("打电话");
  }
}
package com.yu.lock8;

import java.util.concurrent.TimeUnit;
/*
* 3.增加了一个普通的方法后,先执行发短信还是hello?hello
* 4.两个对象,两个同步方法,发短信还是打电话?打电话
* */
public class Test2 {
   public static void main(String[] args) {
       //两个对象,两个调用者,两把锁!
       Phone2 phone1 = new Phone2();
       Phone2 phone2 = new Phone2();

       new Thread(()->{
           phone1.sendSms();
      },"A").start();

       //捕获
       try {
           TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       new Thread(()->{
           phone2.call();
      },"B").start();
  }
}
class Phone2{
   //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("打电话");
  }
   //这里没有锁!不是同步方法,不受锁的影响
   public void hello(){
       System.out.println("hello");
  }
}
package com.yu.lock8;

import java.util.concurrent.TimeUnit;
/*
* 5.增加两个静态的同步方法,只有一个对象,先打印 发短信?打电话?发短信
* 6.两个对象!增加两个静态的同步方法,只有一个对象,先打印 发短信?打电话?发短信
* */
public class Test3 {
   public static void main(String[] args) {
       //两个对象的Class类模板只有一个,static,锁的是Class
       Phone3 phone1 = new Phone3();
       Phone3 phone2 = new Phone3();


       new Thread(()->{
           phone1.sendSms();
      },"A").start();

       //捕获
       try {
           TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       new Thread(()->{
           phone2.call();
      },"B").start();
  }
}
//Phone3唯一一个class对象
class Phone3{
   //synchronized 锁的对象是方法的调用者
   //static 静态方法
   //类一加载就有了! 锁的是Class模板
   public static synchronized void sendSms(){
       try {
           TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println("发短信");
  }
   public static synchronized void call(){
       System.out.println("打电话");
  }
   //这里没有锁!不是同步方法,不受锁的影响

}
package com.yu.lock8;

import java.util.concurrent.TimeUnit;
//1.1个静态的同步方法 1个普通的同步方法,一个对象,先打印 发短信?打电话
//1.1个静态的同步方法 1个普通的同步方法,两个对象,先打印 发短信?打电话
public class Test4 {
   public static void main(String[] args) {
       //两个对象的Class类模板只有一个,static,锁的是Class
       Phone4 phone1 = new Phone4();
       Phone4 phone2 = new Phone4();


       new Thread(()->{
           phone1.sendSms();
      },"A").start();

       //捕获
       try {
           TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       new Thread(()->{
           phone2.call();
      },"B").start();
  }
}
//Phone3唯一一个class对象
class Phone4{
   //synchronized 锁的对象是方法的调用者
   //static 静态方法
   //类一加载就有了! 锁的是Class模板
   //静态的同步方法 锁的Class模板
   public static synchronized void sendSms(){
       try {
           TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println("发短信");
  }
   //普通的同步方法 锁的是调用者
   public synchronized void call(){
       System.out.println("打电话");
  }
   //这里没有锁!不是同步方法,不受锁的影响
//   public void hello(){
//       System.out.println("hello");
//   }
}

小结

new this 具体的一个手机

static Class 唯一的一个模板

6.集合类不安全

package com.yu.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

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

狂神的学习方法推荐; 1.先会用 2.货比三家,寻找其他的解决方案3.看源码

Set不安全

package com.yu.unsafe;

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

/*
* 同理可证:ConcurrentModificationException
*1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
* 2.Set<String> set =new CopyOnWriteArraySet<>();
* */
public class SetTest {
   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();
      }
  }
}

HashSet的底层源码

 public HashSet() {
       map = new HashMap<>();
  }
//add set 本质就是 map key是无法重复的
public boolean add(E e) {
       return map.put(e, PRESENT)==null;
  }
private static final Object PRESENT = new Object();//不变的值

IO,集合,常用类

Map 不安全

package com.yu.unsafe;

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

//ConcurrentModificationException
public class MapTest {
   public static void main(String[] args) {
       //map是这样用的吗? 不是,工作中不用HashMap
       //默认等价于什么? new HashMap<>(16,0.75)
       //Map<String,String> map = new HashMap<>();
       //ConcurrentHashMap原理
       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();
      }
  }
}

7.Callable(简单)

1.可以有返回值

2.可以抛出异常

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

代码测试

package com.yu.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<>()).start();
       //new Thread(new FutureTask<>(Callable)).start();
       new Thread().start();

       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.有缓存

2.结果可能需要等待,会阻塞

8.常用的辅助类(必会)

8.1 CountDownLatch

package com.yu.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");
  }
}

 

原理

countDownLatch.CountDown();//数量减一

countDownLatch.await();//等待计数器归零,然后再向下执行

每次有线程调用countDown()数量减一,假设计数器变为0,countDownLatch.await();就会被唤醒,继续执行。

8.2 CyclicBarrier

加法计数器

package com.yu.add;

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

public class CyclicBarrierDemo {
   /*
   * 收集7颗龙珠召唤神龙
   * 召唤龙珠的线程
   * */
   public static void main(String[] args) {
       CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
           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();
      }
  }
}

8.3 Semaphore

抢车位

6车---3个车位

package com.yu.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(()->{
               try {
                   //acquire() 得到
                   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();
      }
  }
}

原理

semaphore.acquire(); 获得,假设如果已经满了,等待,等待被释放为止;

semaphore.release(); 释放,会将当前的信号量释放 -1,然后唤醒等待的线程

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

9.读写锁

ReadWriteLock

package com.yu.rw;

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

/*
   独占锁(写锁) 一次只能被一个线程占有
   共享锁(读锁) 多个线程可以同时占有
* ReadWriteLock
* 读-读 可以共存
* 读-写 不能共存
* 写-写 不能共存
* */
public class ReadWriteLockDemo {
   public static void main(String[] args) {
       MyCacheLock myCache = new MyCacheLock();

       ///写入
       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();
   //存,写,只希望同时只有一个线程写
   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");
  }
}

10.阻塞队列

阻塞队列

BlockingQueueBlockingQueue不是新的东西

什么情况下我们会使用阻塞队列:多线程并法处理,线程池!

学会使用队列

添加 移除

四组API

方式抛出异常有返回值 不抛出异常阻塞等待超时等待
添加 add offer() put offer(,,)
移除 remove poll() take poll(,)
判断队首元素 element peek    
package com.yu.bq;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.Condition;

public class Test {
   public static void main(String[] args) {
       test1();
  }
   /*
    * 抛出异常
    * */
   public static void test1(){
       //队列的大小
       ArrayBlockingQueue 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("===============");
       //IllegalStateException
       //System.out.println(blockingQueue.add("d"));
       System.out.println(blockingQueue.remove());
       System.out.println(blockingQueue.remove());
       System.out.println(blockingQueue.remove());

       //NoSuchElementException
       System.out.println(blockingQueue.remove());
  }
}
/*
* 有返回值,没有异常
* */
public class Test {
   public static void main(String[] args) {
       test2();
  }
   public static void test1(){
       //队列的大小
       ArrayBlockingQueue 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("===============");
       //IllegalStateException
       //System.out.println(blockingQueue.add("d"));
       System.out.println(blockingQueue.remove());
       System.out.println(blockingQueue.remove());
       System.out.println(blockingQueue.remove());

       //NoSuchElementException
       System.out.println(blockingQueue.remove());
  }
   public static void test2(){
       //队列的大小
       ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
       System.out.println(blockingQueue.offer("a"));
       System.out.println(blockingQueue.offer("b"));
       System.out.println(blockingQueue.offer("c"));
       //false
       System.out.println(blockingQueue.offer("d"));
       System.out.println("===============");

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

       //null
       System.out.println(blockingQueue.poll());
  }
}
/*
    * 等待,阻塞(一直阻塞)
    * */
   public static void test3() throws InterruptedException {
       //队列的大小
       ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
       //一直阻塞
       blockingQueue.put("a");
       blockingQueue.put("b");
       blockingQueue.put("c");
       //blockingQueue.put("d");//队列没有位置了,一直阻塞
       System.out.println(blockingQueue.take());
       System.out.println(blockingQueue.take());
       System.out.println(blockingQueue.take());
       System.out.println(blockingQueue.take());//没有这个元素,一直阻塞

  }
public static void test4() throws InterruptedException {
       //队列的大小
       ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

       blockingQueue.offer("a");
       blockingQueue.offer("b");
       blockingQueue.offer("c");
       blockingQueue.offer("d",2, TimeUnit.SECONDS);//等待超过2秒中就退出
       System.out.println("===========");
       System.out.println(blockingQueue.poll());
       System.out.println(blockingQueue.poll());
       System.out.println(blockingQueue.poll());
       blockingQueue.poll(2,TimeUnit.SECONDS);//等待超过2秒中就退出
  }

 

11.线程池(重点)

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

池化技术

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

线程池,连接池,内存池,对象池////.....创建,销毁,十分浪费资源

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

 

线程池的好处:

1.降低资源的消耗

2.提高响应的速度

3.方便管理

线程复用,可以控制最大并发数,管理线程

线程池:三大方法

package com.yu.pool;

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

public class Demo1 {
   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 < 30; i++) {
               //使用线程之后,使用线程池来拿创建线程
               threadPool.execute(()->{
                   System.out.println(Thread.currentThread().getName()+"ok");
              });
          }
      } catch (Exception e) {
           e.printStackTrace();
      } finally {
           //线程池用完,程序结束,关闭线程池
           threadPool.shutdown();
      }
  }
}

七大参数

public static ExecutorService newSingleThreadExecutor() {
       return new FinalizableDelegatedExecutorService
          (new ThreadPoolExecutor(1, 1,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>()));
  }

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
       return new ThreadPoolExecutor(nThreads, nThreads,
                                     0L, TimeUnit.MILLISECONDS,
                                     new LinkedBlockingQueue<Runnable>(),
                                     threadFactory);
  }

public static ExecutorService newCachedThreadPool() {
       return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                     60L, TimeUnit.SECONDS,
                                     new SynchronousQueue<Runnable>());
  }
//本质ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
                         int maximumPoolSize,
                         long keepAliveTime,
                         TimeUnit unit,
                         BlockingQueue<Runnable> workQueue,
                         ThreadFactory threadFactory,
                         RejectedExecutionHandler handler)
/*
创建一个新的 ThreadPoolExecutor与给定的初始参数.
参数
corePoolSize - 即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut
maximumPoolSize - 池中允许的最大线程数
keepAliveTime - 当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间。
unit - keepAliveTime参数的时间单位
workQueue - 用于在执行任务之前使用的队列。 这个队列将仅保存execute方法提交的Runnable任务。
threadFactory - 执行程序创建新线程时使用的工厂
handler - 执行被阻止时使用的处理程序,因为达到线程限制和队列容量
异常
IllegalArgumentException - 如果以下某项成立:
corePoolSize < 0
keepAliveTime < 0
maximumPoolSize <= 0
maximumPoolSize < corePoolSize
NullPointerException - 如果 workQueue或 threadFactory或 handler为空
*/

手动创建一个线程池

package com.yu.pool;

import java.util.concurrent.*;
/*
* RejectedExecutionException
* new ThreadPoolExecutor.AbortPolicy();银行满了,还有人进来,不处理这个人的,抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy());//哪来的回哪里
* new ThreadPoolExecutor.DiscardPolicy());//队列满了,不会抛出异常
*new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,尝试和最早的区竞争,也不会抛出异常
* */
public class Demo1 {
   public static void main(String[] args) {
       //ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
       //ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定的线程池大小
       //ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的,遇强则强,遇弱则弱
       ExecutorService threadPool = new ThreadPoolExecutor(
               2,
               5,
               3,
               TimeUnit.SECONDS,
               new LinkedBlockingDeque<>(3),
               Executors.defaultThreadFactory(),
               new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,尝试和最早的区竞争,也不会抛出异常
       try {
           for (int i = 1; i <= 10; i++) {
               //使用线程之后,使用线程池来拿创建线程
               threadPool.execute(()->{
                   System.out.println(Thread.currentThread().getName()+"ok");
              });
          }
      } catch (Exception e) {
           e.printStackTrace();
      } finally {
           //线程池用完,程序结束,关闭线程池
           threadPool.shutdown();
      }
  }
}

四种拒绝策略

 public static class CallerRunsPolicy implements RejectedExecutionHandler {
       /**
        * Creates a {@code CallerRunsPolicy}.
        */
       public CallerRunsPolicy() { }

       /**
        * Executes task r in the caller's thread, unless the executor
        * has been shut down, in which case the task is discarded.
        *
        * @param r the runnable task requested to be executed
        * @param e the executor attempting to execute this task
        */
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
           if (!e.isShutdown()) {
               r.run();
          }
      }
  }

/**
    * A handler for rejected tasks that throws a
    * {@code RejectedExecutionException}.
    */
   public static class AbortPolicy implements RejectedExecutionHandler {
       /**
        * Creates an {@code AbortPolicy}.
        */
       public AbortPolicy() { }

       /**
        * Always throws RejectedExecutionException.
        *
        * @param r the runnable task requested to be executed
        * @param e the executor attempting to execute this task
        * @throws RejectedExecutionException always
        */
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
           throw new RejectedExecutionException("Task " + r.toString() +
                                                " rejected from " +
                                                e.toString());
      }
  }

/**
    * A handler for rejected tasks that silently discards the
    * rejected task.
    */
   public static class DiscardPolicy implements RejectedExecutionHandler {
       /**
        * Creates a {@code DiscardPolicy}.
        */
       public DiscardPolicy() { }

       /**
        * Does nothing, which has the effect of discarding task r.
        *
        * @param r the runnable task requested to be executed
        * @param e the executor attempting to execute this task
        */
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
      }
  }

/**
    * A handler for rejected tasks that discards the oldest unhandled
    * request and then retries {@code execute}, unless the executor
    * is shut down, in which case the task is discarded.
    */
   public static class DiscardOldestPolicy implements RejectedExecutionHandler {
       /**
        * Creates a {@code DiscardOldestPolicy} for the given executor.
        */
       public DiscardOldestPolicy() { }

       /**
        * Obtains and ignores the next task that the executor
        * would otherwise execute, if one is immediately available,
        * and then retries execution of task r, unless the executor
        * is shut down, in which case task r is instead discarded.
        *
        * @param r the runnable task requested to be executed
        * @param e the executor attempting to execute this task
        */
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
           if (!e.isShutdown()) {
               e.getQueue().poll();
               e.execute(r);
          }
      }
  }

小结和拓展

池的最大的大小如何去设置

了解:IO密集型,CPU密集型:(调优)

package com.yu.pool;

import java.util.concurrent.*;

public class Demo1 {
   public static void main(String[] args) {
       //自定义线程! 工作 ThreadPoolExecutor

       //最大线程到底该如何定义
       //1.CPU密集型 ,几核,就是几,可以保持CPU效率最高
       //2.IO密集型   判断你程序中十分耗IO的线程,>
       //程序 15个大型任务 io十分占用资源
       //ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
       //ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定的线程池大小
       //ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的,遇强则强,遇弱则弱

       //获得CPU的核数
       System.out.println(Runtime.getRuntime().availableProcessors());
       ExecutorService threadPool = new ThreadPoolExecutor(
               2,
               Runtime.getRuntime().availableProcessors(),
               3,
               TimeUnit.SECONDS,
               new LinkedBlockingDeque<>(3),
               Executors.defaultThreadFactory(),
               new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,尝试和最早的区竞争,也不会抛出异常
       try {
           for (int i = 1; i <= 10; i++) {
               //使用线程之后,使用线程池来拿创建线程
               threadPool.execute(()->{
                   System.out.println(Thread.currentThread().getName()+"ok");
              });
          }
      } catch (Exception e) {
           e.printStackTrace();
      } finally {
           //线程池用完,程序结束,关闭线程池
           threadPool.shutdown();
      }
  }
}

12.四大函数式接口(必须掌握)

新时代的程序员:lambda表达式,链式编程,函数式接口,Stream流式计算

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

@FunctionalInterface
public interface Runnable {
   public abstract void run();
}
//泛型,枚举,反射和注解
//lambda表达式,链式编程,函数式接口,Stream流式计算
//超级多@FunctionalInterface
//简化编程模型,在新版本的框架底层大量应用
//foreach(消费者的函数式接口)

代码测试:

函数式接口

package com.yu.function;

import java.util.function.Function;
/*
* Function 函数型接口,有一个输入参数,有一个输出
* 只要是 表达式简化函数型接口 可以 用lambda
* */
public class Demo01 {
   public static void main(String[] args) {
//       Function<String, String> function = new Function<String, String>() {
//           @Override
//           public String apply(String str) {
//               return str;
//           }
//       };
       Function function = (str)->{return str;};
       System.out.println(function.apply("asd"));
  }
}

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

package com.yu.function;

import java.util.function.Predicate;
/*
* 断定型接口;有一个输入参数,返回值只能是 布尔值!
* */
public class Demo02 {
   public static void main(String[] args) {
       //判断字符串是否为空
//       Predicate<String> predicate = new Predicate<String>() {
//           @Override
//           public boolean test(String str) {
//               return str.isEmpty();
//           }
//       };
       Predicate<String> predicate = (str)->{return str.isEmpty();};
       System.out.println(predicate.test(""));
  }
}

Consumer 消费型接口

package com.yu.function;

import java.util.function.Consumer;
/*
* Consumer 消费型接口,没有返回值
* */
public class Demo03 {
   public static void main(String[] args) {
//       Consumer<String> consumer = new Consumer<String>() {
//           @Override
//           public void accept(String str) {
//               System.out.println(str);
//           }
//       };
       Consumer<String> consumer = (str)->{
           System.out.println(str);
      };
       consumer.accept("asd");
  }
}

Supplier 供给型接口

package com.yu.function;

import java.util.function.Supplier;
/*
* Supplier 供给型接口 没有参数,只有返回值
* */
public class Demo04 {
   public static void main(String[] args) {
//       Supplier<Integer> supplier = new Supplier<Integer>() {
//           @Override
//           public Integer get() {
//               System.out.println("get()");
//               return 1024;
//           }
//       };
       Supplier<Integer> supplier =()->{ return 1024;};
       System.out.println(supplier.get());
  }
}

 

13.Stream流式计算

什么是Stream流式计算

大数据:存储+计算

集合,MySQL本质就是存储东西的

计算都应该交给流来操作

package com.yu.stream;

import java.util.Arrays;
import java.util.List;

/*
* 题目要求:一分钟内完成此题,只能用一行代码实现
* 现在有5个用户!筛选!
*   1.ID必须是偶数
*   2.年龄必须大于23岁
*   3.用户名必须转换为大写字母
*   4.用户名字母倒着排序
*   5.只输出一个用户
* */
public class Test {
   public static void main(String[] args) {
       User u1 = new User(1, "a", 21);
       User u2 = new User(2, "b", 22);
       User u3 = new User(3, "c", 23);
       User u4 = new User(4, "d", 24);
       User u5 = new User(6, "e", 25);
       //集合就是存储
       List<User> list = Arrays.asList(u1, u2, u3, u4, u5);

       //计算交给Stream流
       //lambda表达式,链式编程,函数式接口,Stream流式计算
       list.stream()
              .filter(u->{return u.getId()%2==0;})
              .filter(u->{return u.getAge()>23;})
              .map(u->{return u.getName().toUpperCase();})
              .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
              .limit(1)
              .forEach(System.out::println);
  }
}

14.ForkJoin

什么是ForkJoin

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

大数据:Map Reduce(把大任务分解成小任务)

ForkJoin特点:工作窃取 这里面维护的是双端队列

Forkjoin 怎么用

package com.yu.forkjoin;

import java.util.concurrent.RecursiveTask;

/*
* 求和计算的任务
* 3000 6000(ForkJoin) 9000(Stream并行流)
* 如何调用forkjoin
* 1.forkjoinPool 通过它来执行
* 2.计算任务 forkjoinPool。execute(ForkJoinTask task)
* 3.计算类要继承ForkJoinTask
* */
public class ForkJoinDemo extends RecursiveTask<Long> {

   private Long start;//1
   private Long end;//1990900000

   //临界值
   private Long temp = 10000L;

   public ForkJoinDemo(Long start, Long end) {
       this.start = start;
       this.end = end;
  }

   //计算方法
   @Override
   protected Long compute() {
       if ((end - start) < temp) {
           Long sum = 0L;
           for (Long i = start; i <= end; i++) {
               sum += i;
          }
           return sum;
      } else {//forkjoin 递归
           long middle = (start+end)/2;//平均值
           ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
           task1.fork();//拆分任务,把任务压入线程队列
           ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
           task2.fork();//拆分任务,把任务压入线程队列

           return task1.join()+task2.join();
      }
  }
}

测试类

package com.yu.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
//同一个任务,别人高你几十倍
public class Test {
   public static void main(String[] args) throws ExecutionException, InterruptedException {
       //test1();//5019
       //test2();//6619
       test3();//0
  }
   //普通方法
   public static void test1(){
       long sum = 0L;
       long start = System.currentTimeMillis();
       for (Long i = 1L; i <= 10_0000_0000; i++) {
           sum+= i;
      }
       long end = System.currentTimeMillis();
       System.out.println("sum="+sum+" 时间:"+(end-start));
  }
   //会使用ForkJoin
   public static void test2() throws ExecutionException, InterruptedException {
       long start = System.currentTimeMillis();

       ForkJoinPool forkJoinPool = new ForkJoinPool();
       ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
       ForkJoinTask<Long> submit = forkJoinPool.submit(task);//提交任务
       Long sum = submit.get();
       long end = System.currentTimeMillis();

       System.out.println("sum="+sum+" 时间:"+(end-start));
  }

   public static void test3(){
       long start = System.currentTimeMillis();
       long end = System.currentTimeMillis();
       //Stream并行流
       long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
       System.out.println("sum="+sum+" 时间:"+(end-start));
  }
}

15.异步调用

Future 设计的初衷:对将来的某个事件的结果进行建模

package com.yu.future;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/*
* 异步调用 CompletableFuture
* 异步执行
* 成功回调
* 失败回调
* */
public class Demo01 {
   public static void main(String[] args) throws ExecutionException, InterruptedException {
       //发起一个请求
       //没有返回值的异步回调
//       CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
//           try {
//               TimeUnit.SECONDS.sleep(2);
//           } catch (InterruptedException e) {
//               e.printStackTrace();
//           }
//           System.out.println(Thread.currentThread().getName() + "runAsync=>Void");
//       });
//
//       System.out.println("1111");
//
//       completableFuture.get();//阻塞获取执行结果

       //有返回值的 supplyAsync 异步回调
       //ajax,成功和失败的回调
       //返回的是错误的信息
       CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
           System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
           int i= 10/0;
           return 1024;
      });
       System.out.println(completableFuture.whenComplete((t, u) -> {

           System.out.println("t=>" + t);//正常的返回结果
           System.out.println("u=>" + u);//错误信息:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
      }).exceptionally((e) -> {
           System.out.println(e.getMessage());
           return 233;//可以获取到错误的返回结果
      }).get());
       /*
       * success Code 200
       * error Code 404 500
       * */
  }
}

16.JMM

请你谈谈你对Volatile的理解

1.保证可见性

2.不保证原子性

3.禁止指令重排

什么是JMM

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

关于JMM的一些同步约定

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

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

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

 

线程 工作内存 主内存

8种操作

上面所说的步骤其实就是实现了线程之间的通信,但是不要以为线程之间的通信就是这么简单的,其实在Java中JMM内存模型定义了八种操作来实现同步的细节。

  • read 读取,作用于主内存把变量从主内存中读取到本本地内存。

  • load 加载,主要作用本地内存,把从主内存中读取的变量加载到本地内存的变量副本中

  • use 使用,主要作用本地内存,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。、

  • assign 赋值 作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。

  • store 存储 作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。

  • write 写入 作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

  • lock 锁定 :作用于主内存的变量,把一个变量标识为一条线程独占状态。

  • unlock 解锁:作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。

所以看似简单的通信其实是这八种状态来实现的。

同时在Java内存模型中明确规定了要执行这些操作需要满足以下规则:

  • 不允许read和load、store和write的操作单独出现。

  • 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。

  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。

  • 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。

  • 一个变量在同一时刻只允许一条线程对其进行lock操作,lock和unlock必须成对出现

  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值

  • 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。

  • 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。

所以上面说的操作要严格执行。

问题:程序不知道主内存的值已经被修改过了

17.Volatile

1.保证可见性

 

package com.yu.tvolatile;

import java.util.concurrent.TimeUnit;

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

   public static void main(String[] args) {//main
       new Thread(()->{//线程 1 对主内存的变化不知道的
           while (num==0){

          }
      }).start();

       try {
           TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       num = 1;

       System.out.println(num);
  }
}

2.不保证原子性

原子性:不可分割

线程A在执行任务的时候,不能被打扰,也不能被分割,要么同时成功,要么同时失败。

package com.yu.tvolatile;
//volatile 不保证原子性
public class VDemo2 {

   //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 i1 = 0; i1 < 1000; i1++) {
                   add();
              }
          }).start();
      }

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

       System.out.println(Thread.currentThread().getName()+" "+num);
  }
}

我么们不用 synchronized 或者lock怎么保证原子性呢

使用原子类,解决原子性问题

原子类为什么这么高级

package com.yu.tvolatile;

import java.util.concurrent.atomic.AtomicInteger;

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

   //volatile 不保证原子性
   //原子类 Integer
   private volatile static AtomicInteger num = new AtomicInteger();

   public  static void add(){
       //num++;//不是原子性的操作
       num.getAndIncrement();//AtomicInteger +1 方法 CAS
  }

   public static void main(String[] args) {

       //理论上num的结果应为2万
       for (int i = 1; i <= 20; i++) {
           new Thread(()->{
               for (int i1 = 0; i1 < 1000; i1++) {
                   add();
              }
          }).start();
      }

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

       System.out.println(Thread.currentThread().getName()+" "+num);
  }
}

这些类的底层都是和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在!

3.禁止指令重排

什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的

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

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

int x = 1;//1
int y = 2;//2
x = x+5;//3
y = x*x;//4
//我们所期望的:1234 但是有可能执行的时候变成 2134 1324
//可不可能是 4123!

可能造成影响的结果: a b x y 这四个值默认都是0;

线程A线程B
x=a y=b
b=1 a=2

正常的结果:x=0,y=0,但是由于指令重排

线程A线程B
b=1 a=2
x=a y=b

指令重排诡异的结果: x=2 ; y = 1;

非计算机专业

volatile可以避免指令重排

内存屏障,CPU指令,作用

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

2.可以保证某些变量的的内存可见性(利用这些特性volatile实现了可见性)

Volatile是可以保证可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生

 

18.彻底玩转单例模式

 

饿汉式 DCL懒汉式

package com.yu.single;
//饿汉式单例
public class Hungry {

   //可能会浪费空间
   private byte[] data1 = new byte[1024*1024];
   private byte[] data2 = new byte[1024*1024];
   private byte[] data3 = new byte[1024*1024];
   private byte[] data4 = new byte[1024*1024];

   private Hungry(){

  }

   private final static Hungry HUNGRY = new Hungry();
   public static Hungry getInstance(){
       return HUNGRY;
  }

}

DCL懒汉式

package com.yu.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

//懒汉式单例
//道高一尺魔高一丈
public class LazyMan {
   private static boolean yu = false;
   private LazyMan(){
       synchronized (LazyMan.class){
           if (yu == false){
               yu = true;
          }else {
               throw new RuntimeException("不要试图使用反射破坏异常");
          }
      }
  }

   private volatile static LazyMan lazyMan;

   public static LazyMan getInstance(){
       //双重检测锁模式的 懒汉式单例 DCL懒汉式
       if (lazyMan==null){
           synchronized (LazyMan.class){
               if (lazyMan==null){
                   lazyMan = new LazyMan();//不是一个原子性操作
              }
          }
      }
       return lazyMan;
  }
   //多线程变并发
//   public static void main(String[] args) {
//       for (int i = 0; i < 10; i++) {
//           new Thread(()->{
//               LazyMan.getInstance();
//           }).start();
//       }
//   }
   //反射
   public static void main(String[] args) throws Exception {
       //LazyMan instance = LazyMan.getInstance();

       Field yu = LazyMan.class.getDeclaredField("yu");
       yu.setAccessible(true);
       Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
       declaredConstructor.setAccessible(true);
       LazyMan instance = declaredConstructor.newInstance();

       yu.set(instance,false);

       LazyMan instance2 = declaredConstructor.newInstance();

       System.out.println(instance);
       System.out.println(instance2);

  }
}



/*
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 123
* 132 A
* lazyMan!=null B 此时lazyMan还没有完成构造
* */

静态内部类

package com.yu.single;
//静待内部类
public class Holder {
   private Holder(){

  }

   public static Holder getInstance(){
       return InnerClass.HOLDER;
  }
   public static class InnerClass{
       private static final Holder HOLDER = new Holder();
  }
}

单例不安全,反射

枚举

package com.yu.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

//enum 是一个什么? 本身也是一个Class类
public enum  EnumSingle {

   INSTANCE;
   public EnumSingle getInstance(){
       return INSTANCE;
  }
}
class Test{

   public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
       EnumSingle instance1 = EnumSingle.INSTANCE;
       Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);//Cannot reflectively create enum objects
       declaredConstructor.setAccessible(true);
       EnumSingle instance2 = declaredConstructor.newInstance();

       //NoSuchMethodException com.yu.single.EnumSingle.<init>()
       System.out.println(instance1);
       System.out.println(instance2);
  }
}

无参构造

枚举类型的最终反编译源码

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name:   EnumInstance.java

package com.learn.design.pattern.creational.singleton;


public final class EnumInstance extends Enum
{

   private EnumInstance(String s, int i)
  {
       super(s, i);
  }

   public Object getData()
  {
       return data;
  }

   public void setData(Object data)
  {
       this.data = data;
  }

   public static EnumInstance getInstance()
  {
       return INSTANCE;
  }

   public static EnumInstance[] values()
  {
       EnumInstance aenuminstance[];
       int i;
       EnumInstance aenuminstance1[];
       System.arraycopy(aenuminstance = ENUM$VALUES, 0, aenuminstance1 = new EnumInstance[i = aenuminstance.length], 0, i);
       return aenuminstance1;
  }

   public static EnumInstance valueOf(String s)
  {
       return (EnumInstance)Enum.valueOf(com/learn/design/pattern/creational/singleton/EnumInstance, s);
  }

   public static final EnumInstance INSTANCE;
   private Object data;
   private static final EnumInstance ENUM$VALUES[];

   static
  {
       INSTANCE = new EnumInstance("INSTANCE", 0);
       ENUM$VALUES = (new EnumInstance[] {
           INSTANCE
      });
  }
}

我们一点点看吧,首先我们看一下这个类是final的,这个比较有意思,我们在代码里面是根本就看不出来的,

那继续往下看,

   private EnumInstance(String s, int i)
  {
       super(s, i);
  }

这个是比较符合单例模式的要求的,私有构造器,接着往下看,

   public Object getData()
  {
       return data;
  }

   public void setData(Object data)
  {
       this.data = data;
  }

这个是data的get/set方法,继续,

   public static EnumInstance getInstance()
  {
       return INSTANCE;
  }

这个就是我们写的getInstance方法,继续往下看,

public static final EnumInstance INSTANCE;我们可以看到这个INSTANCE对象,是一个static,并且呢,final的,

那么他在什么时候实例化呢,往下看,

   static
  {
       INSTANCE = new EnumInstance("INSTANCE", 0);
       ENUM$VALUES = (new EnumInstance[] {
           INSTANCE
      });
  }

它是通过静态块的方式,来实例化他,所以本身Enum这个类啊,他在加载的时候,就把INSTANCE这个对象声明好了,

这里声明这个单例对象呢,和饿汉模式非常像,所以呢通过这些点,本身ENUM这个类,是非常符合单例模式的要求的,

那我们从头再看一遍,final代表这个,构造器私有这里

   private EnumInstance(String s, int i)
  {
       super(s, i);
  }

不允许外部进行实例化,同时类变量是静态的,INSTANCE是静态的,没有延迟初始化,通过这个静态块在类加载的时候,

把这个对象初始化完成,所以呢他也是线程安全的,那除了这些之外,还有IO相关的类,反射相关的类,来为枚举类保驾护航,

适当的抛出异常,所以枚举类实现单例模式还是比较优雅的,那关于JAD这个工具,有兴趣的可以多使用他,通过它可以发现

很多有意思的地方,在这里也推荐大家多使用JAD反编译工具,可能能帮你发现一些新大陆,对于某些代码反编译出来,

很容易豁然开朗,那接着回到代码里边,可能枚举类平时使用的不多,项目中使用枚举类也是使用的比较简单的方式,

那这个类里面本身还可以声明方法,那我们现在来领着大家来实战一下

 

 

19.深入理解CAS

 

什么是CAS

大厂必须要深入研究底层!有所突破!修内功,操作系统,计算机网络原理

package com.yu.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)
       //如果我的期望值达到了,那么就更新,否则就不跟更新
       System.out.println(atomicInteger.compareAndSet(2020, 2021));
       System.out.println(atomicInteger.get());

       System.out.println(atomicInteger.compareAndSet(2020, 2021));
       System.out.println(atomicInteger.get());
  }
}

 

Unsafe类

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

缺点

1.循环会耗时

2.一次性只能保证一个共享变量的原子性

3.ABA问题

 

 

CAS:ABA问题(狸猫换太子)

package com.yu.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(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());
  }
}

20.原子引用

 解决ABA问题,引入原子引用 对应的思想:乐观锁

 

带版本号的原子操作!

package com.yu.cas;

import jdk.nashorn.internal.runtime.regexp.joni.constants.TargetInfo;

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

public class CASDemo {
   //AtomicStampedReference 注意:如果泛型是一个包装类,注意对象的引用问题

   //正常的业务操作,这里面比较的都是一个个对象
   static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
   //CAS compareAndSet :比较并更换
   public static void main(String[] args) {
      // AtomicInteger atomicInteger = new AtomicInteger(2020);

       //AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);

       new Thread(()->{
           int stamp = atomicStampedReference.getStamp();//获得版本号
           System.out.println("a1=>"+stamp);

           try {
               TimeUnit.SECONDS.sleep(2);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }

           System.out.println(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=>"+atomicStampedReference.getStamp());

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

 

注意

Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间;

21.各种锁的理解

1.公平锁,非公平锁

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

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

public ReentrantLock(){
   sync = new NonfairSync();
}
public ReentrantLock(boolean fair){
   sync = fair ? new FairSync() : new NonfairSync();
}

2.可重入锁(递归锁)

synchronized
package com.yu.lock;
//synchronized
public class Demo01 {
   public static void main(String[] args) {
       Phone phone = new Phone();

       new Thread(()->{
           phone.sms();
      },"A").start();

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

class Phone{

   public synchronized void sms(){
       System.out.println(Thread.currentThread().getName()+"sms");
       call();//这里也有锁
  }

   public synchronized void call(){
       System.out.println(Thread.currentThread().getName()+"call");
  }
}

Lock

package com.yu.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//Lock
public class Demo02{
   public static void main(String[] args) {
       Phone2 phone = new Phone2();

       new Thread(()->{
           phone.sms();
      },"A").start();

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

class Phone2{
   Lock lock = new ReentrantLock();

   public void sms(){

       lock.lock();//细节问题 lock.lock(); lock.unlock(); Lock锁必须配对,否则就会死在里面
       lock.lock();
       try {
           System.out.println(Thread.currentThread().getName()+"sms");
           call();//这里也有锁
      } catch (Exception e) {
           e.printStackTrace();
      } finally {
           lock.unlock();
           lock.unlock();
      }

  }

   public synchronized void call(){

       lock.lock();

       try {
           System.out.println(Thread.currentThread().getName()+"call");
      } catch (Exception e) {
           e.printStackTrace();
      } finally {
           lock.unlock();
      }

  }
}

3.自旋锁

自定义自旋锁

package com.yu.lock;

import java.util.concurrent.atomic.AtomicReference;

public class SpinlockDemo {

   //int 0
   //Thread null
   AtomicReference<Thread> atomicReference = new AtomicReference<>();

   //加锁
   public void myLock(){
       Thread thread = Thread.currentThread();
       System.out.println(Thread.currentThread().getName()+"==> mylock");

       //自旋锁
       while (!atomicReference.compareAndSet(null,thread)){

      }
  }


   //解锁
   //加锁
   public void myUnLock(){
       Thread thread = Thread.currentThread();
       System.out.println(Thread.currentThread().getName()+"==> myUnlock");
       atomicReference.compareAndSet(thread,null);
  }


}

测试自旋锁

package com.yu.lock;

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

public class TestSpinlock {
   public static void main(String[] args) throws InterruptedException {
//       ReentrantLock reentrantLock = new ReentrantLock();
//       reentrantLock.lock();
//       reentrantLock.unlock();

       //底层使用的自旋锁CAS
       SpinlockDemo lock = new SpinlockDemo();

       new Thread(()->{
           lock.myLock();

           try {
               TimeUnit.SECONDS.sleep(3);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }finally {
               lock.myUnLock();
          }

      },"T1").start();

       TimeUnit.SECONDS.sleep(1);

       new Thread(()->{
           lock.myLock();

           try {
               TimeUnit.SECONDS.sleep(3);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }finally {
               lock.myUnLock();
          }
      },"T2").start();

  }
}

4.死锁

 死锁是什么

package com.yu.lock;

import java.util.concurrent.TimeUnit;

public class DeadLockDemo {
   public static void main(String[] args) {

       String lockA = "lockA";
       String lockB = "lockB";

       new Thread(new MyThread(lockA,lockB),"T1").start();
       new Thread(new MyThread(lockB,lockA),"T2").start();
  }
}
class MyThread implements Runnable{
   private String lockA;
   private String lockB;

   public MyThread(String lockA, String lockB) {
       this.lockA = lockA;
       this.lockB = lockB;
  }

   @Override
   public void run() {
       synchronized (lockA){
           System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get"+lockB);

           try {
               TimeUnit.SECONDS.sleep(2);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }

           synchronized (lockB){
               System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>get"+lockA);
          }
      }
  }
}

解决问题

1.使用 jps -1 定位进程号

2.使用 jstack 进程号 找到死锁问题

 

 

面试,工作中:排查问题;

1.日志 9

2.堆栈 1

 

posted on 2021-05-23 22:50  lsys  阅读(84)  评论(0)    收藏  举报

导航