JUC包下,继续根据资料整理那些不曾注意的问题,有些人云亦云的问题,能在大佬的视频中找到答案,着重看下那些颠覆常识的问题,另外,小马哥对源码的熟悉程度实在令人敬佩。
高并发要关注的问题
1.线程安全
2.减少线程同步竞争
3.合理利用状态位
4.线程池
5.超时意识
1.什么是线程安全问题?
答:多线程并发执行时,对共享内存中共享对象的属性发生修改时所导致的数据冲突问题,称之为线程安全问题
2.线程池:所有的池化操作,我都理解为将要执行的资源放入,减少其创建与销毁的时间,且能动态的去定制获取策略,空闲策略等。
从CountDownLatch,CyclicBarrier,Semaphore入手
以前已经总结过关于这三个线程操作的文章CountDownLatch 与 CyclicBarrier 和Semaphore的区别?现在看下内部操作
CyclicBarrier:
reset:
重置,刷新操作,reset不要轻易去用
breakBarrier :
private void breakBarrier() { generation.broken = true; count = parties; trip.signalAll(); }
nextGeneration:
Updates state on barrier trip and wakes up everyone//更新线程状态并唤醒
项目中用于countdownLatch更多,慎用CyclicBarrire
3.线程池 THREADPOOL
1.线程复用
2.控制最大并发数
3.管理线程
-ExecutorService的实现
1.ThreadPoolExecutor
2.ScheduleThreadPoolExecutor
3.ForkJoinPool
-常见的创建线程池的方式5种
但常用3种,在阿里巴巴开发手册中指定
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。说明:Executors 返回的线程池对象的弊端如下:1)FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。2)CachedThreadPool 和 ScheduledThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
实际谁也不会用到Integer.MAX_VALUE(),但是CacheThreadPool就不一定了,他不会指定线程数量,所以一旦获取线程是循环会有可能到到这个数量
--线程7参
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
在常用3种线程池中只有5参,看下全参
new ThreadPoolExecutor(1, 2, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(3), new ThreadPoolExecutor.AbortPolicy() );
看下idea默认提示
线程7参已经总结过了线程7参
4:如何获取正在运行的线程?
ThreadPoolExecutor是有before,after方法的,但是针对
newCachedThreadPool如何获取呢?
public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); Set<Thread> threads = new HashSet<>(); //计数 setFactory(executorService, threads); for (int i = 0; i < 9; i++) { executorService.submit(() -> { }); } executorService.awaitTermination(10, TimeUnit.MILLISECONDS); // System.out.println("线程池等待 \n"+threads); threads.stream() .filter(Thread::isAlive) .forEach(System.out::println); executorService.shutdown(); }
private static void setFactory(ExecutorService executorService, Set<Thread> threads) { if (executorService instanceof ThreadPoolExecutor) { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService; ThreadFactory threadFactory = threadPoolExecutor.getThreadFactory(); threadPoolExecutor.setThreadFactory(new DelegatingFactory(threadFactory, threads)); } }
private static class DelegatingFactory implements ThreadFactory {
private final ThreadFactory delegate; private Set<Thread> threads;
private DelegatingFactory(ThreadFactory delegate, Set<Thread> threads) { this.delegate = delegate; this.threads = threads; }
@Override public Thread newThread(Runnable r) { Thread thread = delegate.newThread(r); threads.add(thread); return thread; } }
运行结果:
Connected to the target VM, address: '127.0.0.1:50402', transport: 'socket'Thread[pool-1-thread-5,5,main]Thread[pool-1-thread-9,5,main]Thread[pool-1-thread-3,5,main]Thread[pool-1-thread-7,5,main]Thread[pool-1-thread-6,5,main]Thread[pool-1-thread-4,5,main]Thread[pool-1-thread-2,5,main]Thread[pool-1-thread-8,5,main]Thread[pool-1-thread-1,5,main]Disconnected from the target VM, address: '127.0.0.1:50402', transport: 'socket'
颠覆常识的问题
Volatile是保证原子性还是可见性?
这个问题我看资料总结过无数次,都说只能保证内存可见性,但是
volatile即内存屏障,可保证一段内存中一个变量的原子性,原生类型都是原子性的。所以java中 volatile long,volatile double都是线程安全的。
为啥Automic volatiel 用的是int value?而不用boolean?
volatile int scanState; volatile int value;
因为在底层boolean的实现既是int实现的,所以volatile的set方法即安全的
CAS锁是比较偏重的操作?
CAS在操作锁时,执行比较并交换操作,相对synchronized瘦锁是比较重的锁,偏向锁在这里避免了CAS操作。UseBiaseLocking对synchronize有用
总结:这期总结确实颠覆了以往认知,不管是以往的资料还是面试中很难去得到这样的知识点,当然仁者见仁,佩服的是小马哥对底层的理解,可手撕源码实现,这并非一朝一夕的能力,在平时中的书写也可模仿源码的操作,提高代码的可用性。
声明:本文内容根据B站UP主mercyblitz,往期视频以及历史资料整理,扫描二维码关注小马哥公众号,java劝退师,好东西需要分享,干货满满