一、Lock、interface
- Lock lock = new ReentrantLock(); jdk 1.5 后新增的 ReentrantLock 类
- Condition condition = lock.newCondition();
- 1、lock(); //上锁 ,获取锁;释放锁需要在finally
- 2、lockInterruptibly(); //可中断锁,锁被别人抢到,中断等待
- 3、tryLock(); boolean //它表示的是用来尝试一次获取锁:成功获取则返回true;获取失败则返回false。
- 4、tryLock(秒); boolean //多长时间内又没有人占用 //100, TimeUnit.SECONDS
- 5、unlock(); //释放锁
- 唤醒等待机制
- condition.await(); //等待
- condition.signal(); //解除等待
- 公平锁与非公平锁 (new ReentrantLock(boolean))
- 公平锁:线程获取锁的顺序是按照线程启动的顺序来进行分配的,(多个线程按照申请锁的顺序来获取锁)即先来先得FIFO先进先出顺序。
- 非公平锁:一种获取锁的抢占机制,是随机拿到锁的,和公平锁不一样的是先来的不一定先拿到锁,这个方式可能造成某些线程一直拿不到锁,结果就是不公平的。
- ReentrantReadWriteLock类
- 读锁,也成为共享锁。lock.readLock().lock();
- 写锁,也叫排他锁。 lock.writeLock().lock();
- 多个读锁之间不互斥,读锁与写锁互斥,多个写锁互斥。
- 锁的类型:
- 可重入锁:在执行对象中所有同步方法不用再次获得锁,又名递归锁
- 可中断锁:在等待获取锁过程中可中断
- 公平锁: 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁权利
- 读写锁:对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步地写
- synchronized与Lock的区别
-
二、ThreadLocal、并发包
- (创建所有线程的副本;不可见性)
- 每个线程都会有属于自己的本地内存,在堆(也就是上图的主内存)中的变量在被线程使用的时候会被复制一个副本线程的本地内存中,当线程修改了共享变量之后就会通过JMM管理控制写会到主内存中。
- 很明显,在多线程的场景下,当有多个线程对共享变量进行修改的时候,就会出现线程安全问题,即数据不一致问题。常用的解决方法是对访问共享变量的代码加锁(synchronized或者Lock)。但是这种方式对性能的耗费比较大。在JDK1.2中引入了ThreadLocal类,来修饰共享变量,使每个线程都单独拥有一份共享变量,这样就可以做到线程之间对于共享变量的隔离问题。
-

- static ThreadLocal<Object> threadLocal = new ThreadLocal<>(); set(T obj) get() remove() initialValue() 返回此线程局部变量的当前线程的初始值
三、线程池、
cpu 密集:cpu 在同一时间内会频繁操作n个线程
- 线程池的优势(作用):
- 1、降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。线程执行完run 后,不会死亡,等待再次被开启线程
- 2、提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 3、提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
- 线程池的工作原理:
-
- 线程池的7个参数:
- 一、corePoolSize 线程池核心线程大小。
- 线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务。
- 二、maximumPoolSize 线程池最大线程数量。
- 当前线程数达到corePoolSize后,如果继续有任务被提交到线程池,会将任务缓存到工作队列(后面会介绍)中。如果队列也已满,则会去创建一个新线程来出来这个处理。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
- 三、keepAliveTime 空闲线程存活时间。
- 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定
- 四、unit 空闲线程存活时间单位。
- keepAliveTime的计量单位
- 五、workQueue 工作队列:
- 新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
- 1、ArrayBlockingQueue:基于数组的有界阻塞队列
- 按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
- 2、LinkedBlockingQuene:基于链表的无界阻塞队列(其实最大容量为Interger.MAX)
- 按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
- 3、SynchronousQuene:一个不缓存任务的阻塞队列
- 生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
- 4、PriorityBlockingQueue:具有优先级的无界阻塞队列
- 优先级通过参数Comparator实现。
- 如果使用有界队列,当队列饱和时并超过最大线程数时就会执行拒绝策略;而如果使用无界队列,因为任务队列永远都可以添加任务,所以设置 maximumPoolSize 没有任何意义。
- 六、threadFactory 线程工厂。
- 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
- 七、handler 拒绝策略。
- 当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。
- 1、CallerRunsPolicy:在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
- 2、AbortPolicy:直接丢弃任务,并抛出RejectedExecutionException异常。
- 3、DiscardPolicy:直接丢弃任务,什么都不做。
- 4、DiscardOldestPolicy:抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列。
- 一、corePoolSize 线程池核心线程大小。
- 功能线程池:
- 一、newFixedThreadPool:创建固定大小的线程池。
- 每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- 特点:只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的无界队列(最大容量为Interger.MAX)。
- 应用场景:控制线程最大并发数。
- 二、newScheduledThreadPool:创建定时的线程池。
- 创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
- 特点:核心线程数量固定,非核心线程数量无限,执行完闲置 10ms 后回收,任务队列为优先级队列实现的无界阻塞队列。
- 应用场景:执行定时或周期性的任务。
- 三、newCachedThreadPool:创建一个可缓存的线程池。
- 如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
- 特点:无核心线程,非核心线程数量无限,执行完闲置 60s 后回收,任务队列为不存储元素的阻塞队列。
- 应用场景:执行大量、耗时少的任务。
- 四、newSingleThreadExecutor:创建一个单线程的线程池。
- 这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- 特点:只有 1 个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的无界队列(最大容量为Interger.MAX)。
- 应用场景:不适合并发但可能引起 IO 阻塞性及影响 UI 线程响应的操作,如数据库操作、文件操作等。
- 阿里开发手册

- 一、newFixedThreadPool:创建固定大小的线程池。
- 线程池的使用:
- ExecutorService pool=Executors.newCachedThreadPool();
- pool.execute(); //获得线程
- pool.shutdown(); //关闭线程池
摘自:
本文深入解析Java并发编程中的关键概念,包括ReentrantLock的使用,如公平锁与非公平锁,以及Condition的wait和signal方法。详细介绍了线程池的工作原理和四大核心参数,如corePoolSize、maximumPoolSize,以及线程池的四种拒绝策略。此外,还阐述了ThreadLocal的作用和使用场景,如何避免线程安全问题。总结了Java提供的四种线程池类型及其适用场景,如newFixedThreadPool、newScheduledThreadPool等,并给出阿里巴巴开发手册中的线程池最佳实践。



1347

被折叠的 条评论
为什么被折叠?



