2020大厂面试JUC线程重要技术点【集合+线程+阻塞队列+线程池】(下)

简介: 2020大厂面试JUC线程重要技术点【集合+线程+阻塞队列+线程池】(下)

6.3 生产者-消费者案例【新方式】


案例:一个初始值为0的变量,两个线程交替操作,一个加一,一个减一,来5轮


class SahreData{
  private int number = 0;
  private Lock lock = new ReentrantLock();
  private Condition condition = lock.newCondition();
  // 加法
  public void increament(){
    lock.lock{
    try{
    //1.判断
    while(number != 0){
      condition.await(); 
    }
    //2.干活
    number++;
    System.out.println(Thread.currentThread().getName()+ number);
    //3.通知唤醒
    condition.signalAll(); 
    }catch(...){
    }finally{
      lock.unlock();
    } 
    }
  }
  // 减法
  public void decreament(){
    lock.lock{
    try{
    //1.判断
    while(number == 0){
      condition.await(); 
    }
    //2.干活
    number--;
    System.out.println(Thread.currentThread().getName()+ number);
    //3.通知唤醒
    condition.signalAll(); 
    }catch(...){
    }finally{
      lock.unlock();
    } 
    }
  }
}
public class ProdConsumer{
  public static void main(String[] args){
    ShareData shareData = new ShareData();
    new Thread(()->{
      for(int i = 1;i<=5;i++){
        shareData.increament();
      }
    },"A").start();
      new Thread(()->{
      for(int i = 1;i<=5;i++){
        shareData.decreament();
      }
    },"B").start();
  }
}

20200721172246175.png

6.4 虚假唤醒


防止虚假唤醒 一定要用while 不要用if


6.3 中的代码换成If,多添加几个线程就会出现问题!

会出现结果 1 2 -1 0等等,并没有控制住结果。


七、Synchrinized与Lock的区别


1.前者JVM层面,是Java的关键字,后者是API层面,java5以后的出现的。

2. synchronized不可以中断

3. Reentranrlock可以中断,设置超时,或者中断方法

4.synchronized默认非公平锁

5.Reentranrlock可以分组唤醒,精确唤醒

6.synchronized要么随即唤醒一个,要么唤醒全部notify() notifyAll()

实现案例:


多线程之间要按照顺序调用,实现A-B-C三个线程启动:

AA打印5次,BB打印10次,CC打印15次

然后

AA打印5次,BB打印10次,CC打印15次

循环10次


7.1 打印案例【新的Lock版本】

class ShareResource{
  private int number = 1;//A1 B2 C3
  private Lock lock = new ReentrantLock();
  private Condition c1 = new lock.newCondition();
  private Condition c2 = new lock.newCondition();
  private Condition c3 = new lock.newCondition();
  public void prints5(){
    lock.lock();
    try{
      //1.判断
      while(number != 1){
        c1.await();
      }
      //2.干活
      for(int i = 1;i<=5;i++){
    System.out.println(Thread.currentThread().getName()+"\t"+number);
      }
      //3.通知2
      number = 2;
      c2.signal();
    }catch(){}finally{
      lock.unlock();
    }
  }
  public void prints10(){
    lock.lock();
    try{
      //1.判断
      while(number != 2){
        c2.await();
      }
      //2.干活
      for(int i = 1;i<=10;i++){
    System.out.println(Thread.currentThread().getName()+"\t"+number);
      }
      //3.通知2
      number = 3;
      c3.signal();
    }catch(){}finally{
      lock.unlock();
    }
  }
  public void prints15(){
    lock.lock();
    try{
      //1.判断
      while(number != 3){
        c3.await();
      }
      //2.干活
      for(int i = 1;i<=10;i++){
    System.out.println(Thread.currentThread().getName()+"\t"+number);
      }
      //3.通知2
      number = 1;
      c1.signal();
    }catch(){}finally{
      lock.unlock();
    }
  }
}
public class SyncAndReentrantLockDemo{
  ShareSource shareSource = new ShareSource();
  new Thread(()->{
    for(int i=0;i<=10;i++){
      shareSource.prints5();
    }
  },"A").start();
  new Thread(()->{
    for(int i=0;i<=10;i++){
      shareSource.prints10();
    }
  },"B").start();
  new Thread(()->{
    for(int i=0;i<=10;i++){
      shareSource.prints15();
    }
  },"C").start();
}

7.2 生产消费案例【阻塞队列版本】高并发

class MyResource{
  private volatile boolean FLAG = true;//默认开启,生产+消费
  private AtomicInteger atomicInteger = new AtomicInteger();
  BlockingQueue<String> blockingQueue = null;
  public MyResource(BlockingQueue<String> blockingQueue){
    this.blockingQueue = blockingQueue;
    System.out.println(blockingQueue.getClass().getName());
  }
  public void myProd(){
    String data = null;
    boolean retValue;
    while(FLAG){
      data = atomInteger.incrementAndGet()+"";
      retValue = blockingQueue.offer(data,2L,TimeUnit.SECONDS);
    if(retValue){
      System.out.println(Thread.currentThread().getName()+"\t 插入队列"+data+"成功");
    }else{
      System.out.println(Thread.currentThread().getName()+"\t 插入队列"+data+"失败");
    }
    TimeUint.SECONDS.sleep(1);
    }
    System.out.println(Thread.currentThread().getName()+"\t 生产叫停,false 生产结束");
  }
  public void MyConsumer(){
  String result = null;
    while(FLAG){
      result = blockingQueue.poll(2L,TimeUnit.SECONDS);
      if(null == result || result.equalsIngoreCase("")){
        FLAG = false;
        System.out.println(Thread.currentThread().getName()+"\t 超过2S没有消费,消费退出");
        return;
      }
      System.out.println(Thread.currentThread().getName()+"\t 消费队列"+result+"成功");
    }
  }
  public void stop(){
    this.FLAG = fasle;
  }
}
public class ProdConsumer_BlockQueueDemo{
  public static void main(String[] args){
    MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));
    new Thread(()->{
      System.out.println(Thread.currentThread().getName()+ "\t 生产线成启动");
      myResource.myProd();
    },"Prod").start();
    new Thread(()->{
      System.out.println(Thread.currentThread().getName()+ "\t 消费线成启动");
      myResource.myConsumer();
    },"Consumer").start();
  //暂停一会
  try{TimeUnit.SECONDS.sleep(5);catchh(...){}}
  System.out.println("5S结束,大老板叫停,活动结束");
  myResource.stop();
  }
}

20200722093755975.png


八、线程池


8.1 Runnable与 Callable

class MyThread implements Runnable{
  @Override
  public void run(){
  }
}
class MyThread2 implements Callable<Integer>{
  @Override
  public Integer call() throws Exception{
    System.out.println("进来了");
    return 1024;
  }
}
public class CallableDemo{
public static void main(String[] args){
  FuterTask<Integer> futerTask = new FuterTask<>(new MyThread);
  Thread t1 = new Thread(futerTask,"AAA");
  t1.start();
  int result01 = 100;
  int result02 = futerTask.get();
  System.out.println(result01+result02);
 }
}

结果:1124


8.2 线程池的优势


Executor顶级接口和工具包Executors

20200722143801641.png



底层:阻塞队列


1.降低资源消耗

2.提高响应速度

3.提高线程的可管理性

// 拓展工具类
// Array Arrays
// Collection Collections
// Executor Executors


8.3 线程池实现的方式【3种核心】


20200722144322929.png

工作中你用那个??? 哪个都不用的

阿里巴巴开发手册:不允许使用Executors区创建,而是使用ThreadPoolExecutor的方式,这样的处理方式更加明确线程池的运行规则,避免资源耗尽。


20200722164118815.png


但是也要学习!!!!!!如下:


第一种【重要】:


public class MyThreadPoolDemo{
  public static void main(String[] args){
    // 1个线程池 5个线程
    ExecutorService threadPool = Executors.newFixedThreadPool(5);
    try{
      for(int i = 1;i<=10;i++){//10个用户
        threadPool.execute(()->{
          System.out.println(Thread.currentThread().getName()+"办理业务");
        });
      }
    }catch(...){
    }finally{
      thread.shutdown();
    }
  }
}

20200722145120484.png


第二种:


public class MyThreadPoolDemo{
  public static void main(String[] args){
    // 1个线程池 1个线程
    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    try{
      for(int i = 1;i<=10;i++){//10个用户
        threadPool.execute(()->{
          System.out.println(Thread.currentThread().getName()+"办理业务");
        });
      }
    }catch(...){
    }finally{
      thread.shutdown();
    }
  }
}

20200722145316943.png


第三种:


public class MyThreadPoolDemo{
  public static void main(String[] args){
    // 1个线程池 不定线程
    ExecutorService threadPool = Executors.newCacgedThreadPool();
    try{
      for(int i = 1;i<=10;i++){//10个用户
        threadPool.execute(()->{
          System.out.println(Thread.currentThread().getName()+"办理业务");
        });
      }
    }catch(...){
    }finally{
      thread.shutdown();
    }
  }
}

20200722145507650.png


8.4 线程池7大参数

20200722150810617.png

20200722151729871.png

20200722151639341.png

8.5 线程池拒绝策略

20200722160435542.png

8.6 手写线程池【大厂工作核心–7大参数】

public class MyThreadPoolDemo{
  public static void main(String[] args){
    ExecutorService threadPool = new ThreadPoolExecutor(
            2,//核心数
            5,//最大线程数
            1L,//活跃时间
            TimeUint.SECONDS,//单位
            new LinkedBlockingQueue<Runnable>(3),//阻塞队列大小个数
            Executors.defaultThreadFactory(),//线程工厂
            new ThreadPoolExecutor.AbortPolicy());//拒绝策略,会抛异常
    try{
      for(int i = 1;i<=5;i++){
        threadPool.execute(()->{
          System.out.println(Thread.currentThread().getName()+"办理业务");
        });
      }
    }catch(...){
    }finally{
      threadPool.shutdown(); 
    }
  }
}

8.7 如何合理配置线程的数量呢?


回答:


1.CPU密集型?

2.IO密集型?


1.获取CPU密集型


Runtime.getRuntime().getProcessors()


一般为:CPU核数+1个线程


2.IO密集型


io密集型的任务并不是一直在执行任务,应该配置尽可能多的线程


一般为:CPU核数*2

目录
相关文章
|
1天前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
14 1
|
1天前
|
Java 程序员 数据库
Java线程池让使用线程变得更加高效
使用一个线程需要经过创建、运行、销毁三大步骤,如果业务系统每个线程都要经历这个过程,那会带来过多不必要的资源消耗。线程池就是为了解决这个问题而生,需要时就从池中拿取,使用完毕就放回去,池化思想通过复用对象大大提高了系统的性能。线程池、数据库连接池、对象池等都采用了池化技术,下面我们就来学习下线程池的核心知识、面试重点~
61 5
Java线程池让使用线程变得更加高效
|
1天前
|
Java
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
21 0
|
1天前
|
设计模式 消息中间件 安全
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
11 0
|
1天前
|
消息中间件 监控 前端开发
面试官:核心线程数为0时,线程池如何执行?
线程池是 Java 中用于提升程序执行效率的主要手段,也是并发编程中的核心实现技术,并且它也被广泛的应用在日常项目的开发之中。那问题来了,如果把线程池中的核心线程数设置为 0 时,线程池是如何执行的? 要回答这个问题,我们首先要了解在正常情况下,线程池的执行流程,也就是说当有一个任务来了之后,线程池是如何运行的? ## 1.线程池的执行流程 正常情况下(核心线程数不为 0 的情况下)线程池的执行流程如下: 1. **判断核心线程数**:先判断当前工作线程数是否大于核心线程数,如果结果为 false,则新建线程并执行任务。 2. **判断任务队列**:如果大于核心线程数,则判断任务队列是否
21 1
面试官:核心线程数为0时,线程池如何执行?
|
1天前
|
监控 安全 Java
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
|
1天前
|
存储 Java
面试官:素有Java锁王称号的‘StampedLock’你知道吗?我:这什么鬼?
面试官:素有Java锁王称号的‘StampedLock’你知道吗?我:这什么鬼?
43 23
|
1天前
|
存储 安全 Java
大厂面试题详解:java中有哪些类型的锁
字节跳动大厂面试题详解:java中有哪些类型的锁
67 0
|
1天前
|
消息中间件 安全 前端开发
字节面试:说说Java中的锁机制?
Java 中的锁(Locking)机制主要是为了解决多线程环境下,对共享资源并发访问时的同步和互斥控制,以确保共享资源的安全访问。 锁的作用主要体现在以下几个方面: 1. **互斥访问**:确保在任何时刻,只有一个线程能够访问特定的资源或执行特定的代码段。这防止了多个线程同时修改同一资源导致的数据不一致问题。 2. **内存可见性**:通过锁的获取和释放,可以确保在锁保护的代码块中对共享变量的修改对其他线程可见。这是因为 Java 内存模型(JMM)规定,对锁的释放会把修改过的共享变量从线程的工作内存刷新到主内存中,而获取锁时会从主内存中读取最新的共享变量值。 3. **保证原子性**:锁
16 1
|
1天前
|
安全 Java 程序员
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
12 0