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月前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
90 2
|
2月前
|
存储 安全 算法
Java面试题之Java集合面试题 50道(带答案)
这篇文章提供了50道Java集合框架的面试题及其答案,涵盖了集合的基础知识、底层数据结构、不同集合类的特点和用法,以及一些高级主题如并发集合的使用。
125 1
Java面试题之Java集合面试题 50道(带答案)
|
3月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
3月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
|
2月前
|
Java C++
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
38 0
|
3月前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
215 6
|
2月前
|
消息中间件 NoSQL 关系型数据库
【多线程-从零开始-捌】阻塞队列,消费者生产者模型
【多线程-从零开始-捌】阻塞队列,消费者生产者模型
34 0
|
4月前
|
设计模式 Java 调度
JUC线程池: ScheduledThreadPoolExecutor详解
`ScheduledThreadPoolExecutor`是Java标准库提供的一个强大的定时任务调度工具,它让并发编程中的任务调度变得简单而可靠。这个类的设计兼顾了灵活性与功能性,使其成为实现复杂定时任务逻辑的理想选择。不过,使用时仍需留意任务的执行时间以及系统的实际响应能力,以避免潜在的调度问题影响应用程序的行为。
95 1
|
4月前
|
Java API 调度
JUC线程池: FutureTask详解
总而言之,FutureTask是Java并发编程中一个非常实用的类,它在异步任务执行及结果处理方面提供了优雅的解决方案。在实现细节方面可以搭配线程池的使用,以及与Callable接口的配合使用,来完成高效的并发任务执行和结果处理。
46 0