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

目录
相关文章
|
8月前
|
缓存 安全 Java
JUC系列之《CountDownLatch:同步多线程的精准发令枪 》
CountDownLatch是Java并发编程中用于线程协调的同步工具,通过计数器实现等待机制。主线程等待多个工作线程完成任务后再继续执行,适用于资源初始化、高并发模拟等场景,具有高效、灵活、线程安全的特点,是JUC包中实用的核心组件之一。
|
8月前
|
设计模式 缓存 安全
【JUC】(6)带你了解共享模型之 享元和不可变 模型并初步带你了解并发工具 线程池Pool,文章内还有饥饿问题、设计模式之工作线程的解决于实现
JUC专栏第六篇,本文带你了解两个共享模型:享元和不可变 模型,并初步带你了解并发工具 线程池Pool,文章中还有解决饥饿问题、设计模式之工作线程的实现
546 2
|
8月前
|
Java 测试技术 API
【JUC】(1)带你重新认识进程与线程!!让你深层次了解线程运行的睡眠与打断!!
JUC是什么?你可以说它就是研究Java方面的并发过程。本篇是JUC专栏的第一章!带你了解并行与并发、线程与程序、线程的启动与休眠、打断和等待!全是干货!快快快!
1158 2
|
8月前
|
设计模式 消息中间件 安全
【JUC】(3)常见的设计模式概念分析与多把锁使用场景!!理解线程状态转换条件!带你深入JUC!!文章全程笔记干货!!
JUC专栏第三篇,带你继续深入JUC! 本篇文章涵盖内容:保护性暂停、生产者与消费者、Park&unPark、线程转换条件、多把锁情况分析、可重入锁、顺序控制 笔记共享!!文章全程干货!
444 1
|
存储 缓存 安全
JUC并发—11.线程池源码分析
本文主要介绍了线程池的优势和JUC提供的线程池、ThreadPoolExecutor和Excutors创建的线程池、如何设计一个线程池、ThreadPoolExecutor线程池的执行流程、ThreadPoolExecutor的源码分析、如何合理设置线程池参数 + 定制线程池。
JUC并发—11.线程池源码分析
|
存储 安全 算法
Java 集合面试题 PDF 下载及高频考点解析
本文围绕Java集合面试题展开,详细解析了集合框架的基本概念、常见集合类的特点与应用场景。内容涵盖`ArrayList`与`LinkedList`的区别、`HashSet`与`TreeSet`的对比、`HashMap`与`ConcurrentHashMap`的线程安全性分析等。通过技术方案与应用实例,帮助读者深入理解集合类的特性和使用场景,提升解决实际开发问题的能力。文末附带资源链接,供进一步学习参考。
311 4
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
543 3
|
数据采集 Java Linux
面试大神教你:如何巧妙回答线程优先级这个经典考题?
大家好,我是小米。本文通过故事讲解Java面试中常见的线程优先级问题。小明和小华的故事帮助理解线程优先级:高优先级线程更可能被调度执行,但并非越高越好。实际开发需权衡业务需求,合理设置优先级。掌握线程优先级不仅能写出高效代码,还能在面试中脱颖而出。最后,小张因深入分析成功拿下Offer。希望这篇文章能助你在面试中游刃有余!
288 4
面试大神教你:如何巧妙回答线程优先级这个经典考题?
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
833 14
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
620 13