java面经自用4
-
synchronized和lock有什么区别?
- synchronized是关键字,jvm层面支持,lock是api层面的锁
- synchronized不需要手动释放,lock需要手动释放不然会死锁(lock(),unlock()方法需要配合try/finally使用)
- synchronized不可中断,lock可中断(设置超时trylock(Long timeout,TimeUnit unit),调用interrupt可中断
ReentrantLock lock = new ReentrantLock(); //获取锁 lock.lockInterruptibly(); //中断 new Thread().interrupt();
- synchronized是不公平锁,lock两者都可以实现boolean为true公平
- synchronized要么随机唤醒一个要么全部唤醒,lock有条件的唤醒利用condition
class bean { private Lock lock = new ReentrantLock(); private Condition c1 = lock.newCondition(); private Condition c2 = lock.newCondition(); private Condition c3 = lock.newCondition(); public void print(int number) { lock.lock(); if (number == 1) { for (int i = 1; i <= 5; i++) { System.out.println(Thread.currentThread().getName() + "==" + i); } c2.signal(); try { c3.await(); } catch (InterruptedException e) { e.printStackTrace(); } } if (number == 2) { for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName() + "==" + i); } c3.signal(); try { c1.await(); } catch (InterruptedException e) { e.printStackTrace(); } } if (number == 3) { for (int i = 1; i <= 15; i++) { System.out.println(Thread.currentThread().getName() + "==" + i); } c1.signal(); try { c2.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class condition { public static void main(String[] args) { bean b = new bean(); new Thread("A") { @Override public void run() { for (int i = 1; i <= 10; i++) { b.print(1); } } }.start(); new Thread("B") { @Override public void run() { for (int i = 1; i <= 10; i++) { b.print(2); } } }.start(); new Thread("C") { @Override public void run() { for (int i = 1; i <= 10; i++) { b.print(3); } } }.start(); }
-
线程池
创建线程4种方式
为什么出现Callable接口?
Callable接口(带返回值的),适合并发,解决阻塞绕过执行时间长的线程
class myth implements Callable<Integer> { @Override public Integer call() throws Exception { //只会调用一次,没必要重复执行 System.out.println(Thread.currentThread().getName()+"====="+"我叼你妈的"); return 1024; } } public class mythread { public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<Integer> task = new FutureTask<>(new myth()); FutureTask<Integer> task2 = new FutureTask<>(new myth()); new Thread(task, "AA").start(); new Thread(task, "BB").start(); int result01=100; int result02=task.get();//建议放在最后使用,也就是说计算完成之后再去调用。如果想提前调用可能会出现自旋,while(!task.idDone)一直判断 System.out.println(result01+result02); } }
5种线程池,常用的有三种
newWorkStealingPool(),java8新推出
newScheduledThreadPool()带调度的,每隔时间段执行
newSingleThreadExecutor()一个任务一个任务执行
newCachedThreadPool()带缓存的,适合执行短期任务,灵活回收线程
newFixedThreadPool(int),固定数线程,执行长期任务执行性能好
都是通过new ThreadPoolExecutor()实现
public static void main(String[] args) { ExecutorService pool1 = Executors.newFixedThreadPool(5);//一池五个线程 ExecutorService pool2 = Executors.newSingleThreadExecutor();//一池一个线程 ExecutorService pool3 = Executors.newCachedThreadPool();//一池N个线程 try { for (int i = 1; i <= 10; i++) { // pool1.execute(new Thread() { // @Override // public void run() { // System.out.println(); // } // }); pool1.execute(() -> { // 只有五个线程去执行 System.out.println(Thread.currentThread().getName()+"执行业务"); }); pool3.execute(()->{ System.out.println(Thread.currentThread().getName()+"执行业务"); }); } } catch (Exception e) { e.printStackTrace(); } finally { pool1.shutdown(); } }
七大参数:
- int corePoolSize:线程池中的常驻核心线程数
- int maximumPoolSize:线程池能够容纳同时执行的最大线程数
- Long KeepAliveTime:多余的空闲线程的存活时间,当线程池数量超过corePoolSize时,当空闲时间达到KeepAliveTime时,多余的空闲线程会被回收到只剩下corePoolSize
- TimeUnit unit:KeepAliveTime的时间单位
- BlockingQueue workQueue 任务队列,被提交暂未执行的任务
- ThreadFactory threadFactory 表示生成线程池中工作线程的线程工厂,用于创建线程
- RejectedExecutionHandler handler 拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数
4中拒绝策略:
AbortPolicy:直接抛出RejectedExecutionHandler异常,阻止系统正常运行
CallerRunsPolicy:将某些任务回退到调用者(main )
DiscardOldestPolicy:抛弃队列中等待最久的任务,把当前任务加入到队列中并尝试再次提交当前任务
DiscardPolicy:直接丢弃任务,无不给予任何处理也不抛出异常
以上策略均实现RejectedExecutionHandler接口,以上线程池在实际开发中并不会使用而是我们自定义线程池
-
自定义线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor( 2, 5, 1L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() ); //核心线程数为2,同时运行的最大线程数5(包括核心线程数),空闲队列的生存时间,时间的单位,创建线程的工厂,拒绝策略的设置 public static void main(String[] args) { ThreadPoolExecutor pool = new ThreadPoolExecutor( 2, 5, 1L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() ); try { for (int i = 1; i <= 10; i++) { // pool1.execute(new Thread() { // @Override // public void run() { // System.out.println(); // } // }); // 最大线程池为5+3=8,但是10个请求则报错 pool.execute(() -> { // 只有五个线程去执行 System.out.println(Thread.currentThread().getName()+"执行业务"); }); } } catch (Exception e) { e.printStackTrace(); } finally { pool.shutdown(); } }
-
如何配置合理线程数
按照业务进行选择:CPU密集型,IO密集型
首先调用Runtime.getRuntime().availableProcessors()后
CPU密集型:
线程数=CPU核数+1
IO密集型分两种情况
- 由于Io密集型任务线程并不是一直在运行任务,则配置尽可能多的线程,线程数= CPU核数*2
- 线程数=CPU核数/1-阻塞系数,阻塞系数在0.8-0.9
-
产生死锁的主要原因?
指两个或以上的进程在执行过程中,因为争夺资源而造成的一种互相等待的现象,若无外力干涉则永远推进下去,如果系统资源充足,进程的资源都本分得到满足,死锁出现的可能性就很低,否则就会因为争夺资源而陷入死锁
手写死锁:
class lock implements Runnable { private String lockA; private String lockB; public lock(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA) { System.out.println(Thread.currentThread().getName()+"持有"+lockA+"尝试获取"+lockB); synchronized (lockB){ System.out.println(Thread.currentThread().getName()+"持有"+lockB+"尝试获取"+lockA); } } } } public class dieLock { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; new Thread(new lock(lockA, lockB), "ThreadAAA").start(); new Thread(new lock(lockB, lockA), "ThreadBBB").start(); } } public class dieLock { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; new Thread(new lock(lockA, lockB), "ThreadAAA").start(); new Thread(new lock(lockB, lockA), "ThreadBBB").start(); } }
``