zoukankan      html  css  js  c++  java
  • 阳哥讲面试题(二)队列,线程池

    一、概述

    image
    image
    image

    二、为什么用,有什么好处

    image
    image

    三、BlockingQueue的核心方法

    image
    image

    四、SynchronousQueue队列

    理论

    image

    实操

    public class SynchronousQueueDemo {
    
    	public static void main(String[] args) {
    		
    		BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
    		new Thread(() -> {
    			try {
    				System.out.println(Thread.currentThread().getName() + "	 put 1");
    				blockingQueue.put("1");
    				
    				System.out.println(Thread.currentThread().getName() + "	 put 2");
    				blockingQueue.put("2");
    				
    				System.out.println(Thread.currentThread().getName() + "	 put 3");
    				blockingQueue.put("3");
    				
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		},"线程1").start();
    		
    		new Thread(() -> {
    			try {
    				TimeUnit.SECONDS.sleep(5);
    				System.out.println(Thread.currentThread().getName() + "	 get " + blockingQueue.take());
    				
    				TimeUnit.SECONDS.sleep(5);
    				System.out.println(Thread.currentThread().getName() + "	 get " + blockingQueue.take());
    				
    				TimeUnit.SECONDS.sleep(5);
    				System.out.println(Thread.currentThread().getName() + "	 get " + blockingQueue.take());
    			
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		},"线程2").start();
    	}
    }
    

    首先put1,因为该队列只能存放一个元素,所以后边的put2和put3只能等待;5s后,take1,取出元素,队列为空,然后put2,以此类推。

    • 输出:
    线程1	 put 1
    线程2	 get 1
    线程1	 put 2
    线程2	 get 2
    线程1	 put 3
    线程2	 get 3
    

    五、线程通信之生产者与消费者

    传统版

    /**
     * TODO 一个初始值为0的变量,两个线程对其交替操作,一个加1,一个减1,来五轮。
     * 
     * @author kakaluote
     * @date 2021年6月30日 下午4:23:33
     * 
     * 1、线程 操纵 资源类
     * 2、判断 干活 唤醒通知
     * 3、严防多线程下的虚假唤醒
     * 
     *  深 透 明 细
     */
    public class ProConsu_TraditionDemo {
    
    	public static void main(String[] args) {
    		ShareData shareData = new ShareData();
    		new Thread(() -> {
    			for (int i = 1; i <= 5; i++) {
    				try {
    					shareData.increment();
    				} catch (Exception e) {
    					
    				}
    			}
    		},"线程1").start();
    		new Thread(() -> {
    			for (int i = 1; i <= 5; i++) {
    				try {
    					shareData.decrement();
    				} catch (Exception e) {
    					
    				}
    			}
    		},"线程2").start();
    	}
    }
    
    //资源类
    class ShareData{
    	private int number = 0;
    	private Lock lock = new ReentrantLock();
    	private Condition condition = lock.newCondition();
    	
    	public void increment() throws Exception{
    		lock.lock();
    		try{
    			//1 判断
    			while(number != 0){
    				//等待,不能生产
    				condition.await();
    			}
    			//2、干活
    			number ++;
    			System.out.println(Thread.currentThread().getName() + "	生产	" + number);
    			//3、通知唤醒
    			condition.signalAll();
    		}catch(Exception e){
    			
    		}finally{
    			lock.unlock();
    		}
    	}
    	
    	public void decrement() throws Exception{
    		lock.lock();
    		try{
    			//1 判断
    			while(number == 0){
    				//等待,不能消费
    				condition.await();
    			}
    			//2、干活
    			number --;
    			System.out.println(Thread.currentThread().getName() + "	消费	" + number);
    			//3、通知唤醒
    			condition.signalAll();
    		}catch(Exception e){
    			
    		}finally{
    			lock.unlock();
    		}
    	}
    }
    

    输出

    线程1	生产	1
    线程2	消费	0
    线程1	生产	1
    线程2	消费	0
    线程1	生产	1
    线程2	消费	0
    线程1	生产	1
    线程2	消费	0
    线程1	生产	1
    线程2	消费	0
    

    阻塞队列版

    public class ProdConsumer_BlockQueueDemo {
    
    	public static void main(String[] args) {
    		
    		MyResource myResource = new MyResource(new ArrayBlockingQueue<String>(10));
    		new Thread(() -> {
    			System.out.println(Thread.currentThread().getName() + "	 启动成功");
    			try {
    				myResource.myProd();
    			} catch (Exception e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		},"生产者").start();
    		new Thread(() -> {
    			System.out.println(Thread.currentThread().getName() + "	 启动成功");
    			System.out.println();
    			try {
    				myResource.myConsumer();
    			} catch (Exception e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		},"消费者").start();
    		
    		
    		try {
    			TimeUnit.SECONDS.sleep(5);
    		} catch (InterruptedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		System.out.println();
    		
    		System.out.println("5s时间到,大老板" + Thread.currentThread().getName() + "叫停,活动结束");
    		myResource.stop();
    	}
    }
    
    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() throws Exception{
    		String data = null;
    		boolean retValue;
    		while(FLAG) {
    			//++i
    			data = atomicInteger.incrementAndGet() + "";
    			retValue = blockingQueue.offer(data,2L,TimeUnit.SECONDS);
    			if(retValue) {
    				System.out.println(Thread.currentThread().getName() + "	插入队列" + data + "成功!");
    			}else {
    				System.out.println(Thread.currentThread().getName() + "	插入队列" + data + "失败!");
    			}
    			TimeUnit.SECONDS.sleep(1);
    		}
    		System.out.println(Thread.currentThread().getName() + "	 大老板说:FLAG=false,生产结束!");
    	}
    	
    	public void myConsumer()throws Exception{
    		String result = null;
    		while(FLAG) {
    			result = blockingQueue.poll(2L,TimeUnit.SECONDS);
    			if(null == result || result.equalsIgnoreCase("")) {
    				FLAG = false;
    				System.out.println(Thread.currentThread().getName() + "	 超过2s没有取到,消费退出。");
    				return;
    			}
    			System.out.println(Thread.currentThread().getName() + "	消费队列" + result + "成功!");
    		}
    	}
    	
    	public void stop(){
    		this.FLAG = false;
    	}
    }
    

    输出:

    java.util.concurrent.ArrayBlockingQueue
    生产者	 启动成功
    消费者	 启动成功
    
    生产者	插入队列1成功!
    消费者	消费队列1成功!
    生产者	插入队列2成功!
    消费者	消费队列2成功!
    生产者	插入队列3成功!
    消费者	消费队列3成功!
    生产者	插入队列4成功!
    消费者	消费队列4成功!
    生产者	插入队列5成功!
    消费者	消费队列5成功!
    
    5s时间到,大老板main叫停,活动结束
    生产者	 大老板说:FLAG=false,生产结束!
    消费者	 超过2s没有取到,消费退出。
    

    六、synchronized和lock的区别

    image
    image
    image
    image
    image

    /**
     * TODO 多线程之间按顺序调用,实现A->B->C三个线程启动,要求如下:
     * AA打印5次,BB打印10次,CC打印15次
     * 紧接着
     * AA打印5次,BB打印10次,CC打印15次
     * ...
     * 来10轮
     * @author kakaluote
     * @date 2021年7月1日 上午10:52:33
     */
    public class SyncAndReentrantLockDemo {
    
    	public static void main(String[] args) {
    		
    		ShareResource shareResource = new ShareResource();
    		
    		new Thread(() -> {
    			for (int i = 1; i <= 10; i++) {
    				shareResource.print5();
    			}
    		},"线程A").start();
    		new Thread(() -> {
    			for (int i = 1; i <= 10; i++) {
    				shareResource.print10();
    			}
    		},"线程B").start();
    		new Thread(() -> {
    			for (int i = 1; i <= 10; i++) {
    				shareResource.print15();
    			}
    		},"线程C").start();
    	}
    }
    
    class ShareResource{
    	private int number = 1;//a,1;b,2;c,3
    	private Lock lock = new ReentrantLock();
    	private Condition c1 = lock.newCondition();
    	private Condition c2 = lock.newCondition();
    	private Condition c3 = lock.newCondition();
    	
    	public void print5(){
    		lock.lock();
    		try{
    			while(number != 1){
    				c1.await();
    			}
    			for (int i = 1; i <= 5; i++) {
    				System.out.println(Thread.currentThread().getName() + "	" + i);
    			}
    			number = 2;
    			c2.signal();
    		}catch(Exception e){
    			
    		}finally{
    			lock.unlock();
    		}
    	}
    	
    	public void print10(){
    		lock.lock();
    		try{
    			while(number != 2){
    				c2.await();
    			}
    			for (int i = 1; i <= 10; i++) {
    				System.out.println(Thread.currentThread().getName() + "	" + i);
    			}
    			number = 3;
    			c3.signal();
    		}catch(Exception e){
    			
    		}finally{
    			lock.unlock();
    		}
    	}
    	
    	public void print15(){
    		lock.lock();
    		try{
    			while(number != 3){
    				c3.await();
    			}
    			for (int i = 1; i <= 15; i++) {
    				System.out.println(Thread.currentThread().getName() + "	" + i);
    			}
    			number = 1;
    			c1.signal();
    		}catch(Exception e){
    			
    		}finally{
    			lock.unlock();
    		}
    	}
    }
    

    七、Callable接口

    demo (创建线程的第三种方式)

    public class CallableDemo {
    
    	public static void main(String[] args) {
    		
    		//同一个FutureTask,多个线程同时启动,call方法只会执行一次
    		FutureTask<Integer> task = new FutureTask<Integer>(new MyThread());
    		new Thread(task,"AAA").start();
    		new Thread(task,"BBB").start();
    		
    		//可以通过此方式判断任务是否执行完
    		while(task.isDone()){
    			
    		}
    		try {
    			//get每次最好放在最后,因为他会阻塞后边的线程
    			Integer integer = task.get();
    			System.out.println(integer);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    class MyThread implements Callable<Integer>{
    
    	@Override
    	public Integer call() throws Exception {
    		System.out.println(Thread.currentThread().getName() + " :Callable");
    		return 777;
    	}
    }
    

    八、线程池

    为什么用?优势?

    image

    线程池如何使用?

    架构说明

    image
    image
    image

    编码实现(三大常用线程池)

    • Executors.newFixedThreadPool(5)
      image
    //模拟银行的五个窗口(1池五个处理线程)
    ExecutorService threadPool = Executors.newFixedThreadPool(1);
    try {
    	//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
    	for (int i = 1; i < 10; i++) {
    		threadPool.execute(() -> {
    			System.out.println(Thread.currentThread().getName() + "	 办理业务");
    		});
    	}
    }catch(Exception e) {
    	
    }finally {
    	threadPool.shutdown();
    }
    

    输出:

    pool-1-thread-1	 办理业务
    pool-1-thread-4	 办理业务
    pool-1-thread-3	 办理业务
    pool-1-thread-2	 办理业务
    pool-1-thread-3	 办理业务
    pool-1-thread-4	 办理业务
    pool-1-thread-1	 办理业务
    pool-1-thread-5	 办理业务
    pool-1-thread-2	 办理业务
    
    • Executors.newSingleThreadExecutor()
      image
    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    try {
    	//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
    	for (int i = 1; i < 10; i++) {
    		threadPool.execute(() -> {
    			System.out.println(Thread.currentThread().getName() + "	 办理业务");
    		});
    	}
    }catch(Exception e) {
    	
    }finally {
    	threadPool.shutdown();
    }
    

    输出:

    pool-1-thread-1	 办理业务
    pool-1-thread-1	 办理业务
    pool-1-thread-1	 办理业务
    pool-1-thread-1	 办理业务
    pool-1-thread-1	 办理业务
    pool-1-thread-1	 办理业务
    pool-1-thread-1	 办理业务
    pool-1-thread-1	 办理业务
    pool-1-thread-1	 办理业务
    
    • Executors.newCachedThreadPool()
      image
    ExecutorService threadPool = Executors.newCachedThreadPool();
    try {
    	//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
    	for (int i = 1; i < 10; i++) {
    		threadPool.execute(() -> {
    			System.out.println(Thread.currentThread().getName() + "	 办理业务");
    		});
    	}
    }catch(Exception e) {
    	
    }finally {
    	threadPool.shutdown();
    }
    

    输出:

    pool-1-thread-3	 办理业务
    pool-1-thread-6	 办理业务
    pool-1-thread-1	 办理业务
    pool-1-thread-4	 办理业务
    pool-1-thread-2	 办理业务
    pool-1-thread-7	 办理业务
    pool-1-thread-5	 办理业务
    pool-1-thread-8	 办理业务
    pool-1-thread-9	 办理业务
    

    image

    线程池的重要参数介绍(7大参数)

    7大参数

    image

    image
    image

    • corePoolSize
      image
    • keepAliveTime
      image

    image

    线程池的底层工作原理

    image
    image

    生产上合理设置线程池参数

    线程池的拒绝策略

    • 是什么
      image
    • JDK内置的拒绝策略
      image
      image
      image
      image
    • 四种拒绝策略都实现了 RejectedExecutionHandler

    不能用固定,单个,可缓存的线程池创建

    image
    image

    工作中使用线程池,自定义

    • ThreadPoolExecutor.AbortPolicy(),直接抛出异常
    ExecutorService threadPool = new ThreadPoolExecutor(2, 5, 1L, 
    		TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3),Executors.defaultThreadFactory(),
    		new ThreadPoolExecutor.AbortPolicy());
    try {
    	//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
    	for (int i = 1; i < 10; i++) {
    		threadPool.execute(() -> {
    			System.out.println(Thread.currentThread().getName() + "	 办理业务");
    		});
    	}
    }catch(Exception e) {
    	e.printStackTrace();
    }finally {
    	threadPool.shutdown();
    }
    

    image

    • ThreadPoolExecutor.CallerRunsPolicy(),将某些任务回退到调用者
      image
    • ThreadPoolExecutor.DiscardPolicy(),直接丢弃(只能完成max+队列个数)
      image
    • ThreadPoolExecutor.DiscardOldestPolicy(),抛弃等待最久的
      image

    合理配置线程池(针对的是maxPoolSize)

    cpu核数:Runtime.getRuntime().availableProcessors()

    如果是CPU密集型

    image

    如果是IO密集型(有两种情况)

    • 1
      image
    • 2
      image

    九、死锁编码以及定位分析

    是什么

    产生死锁的原因

    image

    image

    代码

    public class DeadLockDemo {
    
    	public static void main(String[] args) {
    		String lockA = "lock1";
    		String lockB = "lock2";
    		
    		new Thread(new HoldLockThread(lockA,lockB),"Thread_AAA").start();
    		new Thread(new HoldLockThread(lockB,lockA),"Thread_BBB").start();
    	}
    }
    
    class HoldLockThread implements Runnable{
    
    	private String lockA;
    	private String lockB;
    	
    	public HoldLockThread(String lockA, String lockB) {
    		this.lockA = lockA;
    		this.lockB = lockB;
    	}
    
    	@Override
    	public void run() {
    		
    		synchronized (lockA) {
    			System.out.println(Thread.currentThread().getName() + "	 自己持有:" + lockA + "	 尝试获得:" + lockB);
    			try {
    				TimeUnit.SECONDS.sleep(2);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			synchronized (lockB) {
    				
    			}
    		}
    	}
    }
    

    输出:
    image

    解决

    定位死锁

    image

    image

    image

    image

  • 相关阅读:
    Encountered a section with no Package: header
    java的System.getProperty()方法能够获取的值
    谷歌技术&quot;三宝&quot;之MapReduce
    surfaceDestroyed什么时候被调用
    调用CachedRowSetImpl类时,为什么会出现这样的错误
    正則表達式举例(下)
    java中接口的定义与实现
    UVA 538
    编程算法
    3D空间包围球(Bounding Sphere)的求法
  • 原文地址:https://www.cnblogs.com/kaka-qiqi/p/14954952.html
Copyright © 2011-2022 走看看