Java多线程之线程池,volatile,悲观锁,乐观锁,并发工具类
1.线程池核心原理
①创建一个空的池子
②提交任务时,尺子会创建新的线程对象,任务执行完毕后,线程会归还给池子。
下次提交任务时,就不需要创建新的线程,直接复用已有的线程即可。
③但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待。
1.创建线程池
Executors
:线程池工具类通过调用方法返回不同类型的线程池对象。
创建一个上限为3的线程池:
public class Main{
public static void main(String[] args) {
//获取线程池对象
ExecutorService pools = Executors.newFixedThreadPool(3);
//提交任务
pools.submit(new MyRunnable());
pools.submit(new MyRunnable());
pools.submit(new MyRunnable());
//销毁线程池
pools.shutdown();
}
}
2.任务拒绝策略
自定义线程池执行优先级分贝为:核心线程,临时线程,阻塞队列。
3.自定义线程池
public class Main{
public static void main(String[] args) {
//创建自定义线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3,//核心线程数量,不能小于0
6,//最大线程数,要大于核心线程数
60,//空闲线程最大存活时间
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(3),//任务队列
Executors.defaultThreadFactory(),//创建线程工厂
new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略
);
}
}
2.线程池的大小
1.最大并行数
最大并行数指的是在线程池中同时执行的最大线程数量。
当线程池中的活动线程数量达到最大并行数时,新的任务会进入等待队列,直到有可用的线程资源。
通过控制最大并行数,可以有效地管理线程池的负载,避免过多的并发线程导致系统资源耗尽。
查看电脑的最大线程数量
//利用java虚拟机查看可用处理器的数目
System.out.println(Runtime.getRuntime().availableProcessors());
2.影响线程池大小的因素
根据项目的运算需求决定:
①CPU密集型
运算:
线程池大小 = 最大并行数 + 1 线程池大小=最大并行数+1 线程池大小=最大并行数+1
②I/O密集型
运算:
最大并行数 ? 期望 C P U 利用率 ? 总时间( C P U 计算时间 + 等待时间) C P U 计算时间 最大并行数*期望CPU利用率*\frac{总时间(CPU计算时间+等待时间)}{CPU计算时间} 最大并行数?期望CPU利用率?CPU计算时间总时间(CPU计算时间+等待时间)?
3.多线程常见考点(volatile,悲观锁,乐观锁)
①
volatile关键字
:
volatile是一个Java关键字,用于修饰变量。
当一个变量被声明为volatile时,意味着这个变量的值可能会被多个线程同时修改。
volatile关键字的作用是告诉编译器和JVM,对于这个变量的读写操作不能进行重排序,且每次读取该变量时都必须从内存中读取最新的值,而不是使用缓存。
这样可以保证多个线程能够正确地读取到最新的值
,从而避免了数据不一致的问题。
②
悲观锁
:
悲观锁是一种线程同步机制
,用于确保在多线程环境下对共享资源的安全访问。
悲观锁的基本思想是假设在未来的某个时间点会有其他线程来访问共享资源,因此通过阻塞和限制其他线程的访问以确保数据的一致性和完整性
。
在悲观锁的实现中,当一个线程访问共享资源时,会将该资源加锁,以阻塞其他线程对该资源的访问。
只有当当前线程完成对资源的操作后,其他线程才能获取到锁,然后才能访问资源。
悲观锁的特点是,在多线程环境下会频繁地加锁和释放锁,这会导致线程切换的开销增加。
因此,悲观锁适用于对共享资源的访问频率较低的情况,以减少线程之间的竞争。
常见的悲观锁实现包括互斥锁(Mutex)和读写锁(ReadWriteLock)。
互斥锁可以确保同一时间只有一个线程能够访问共享资源,而读写锁在读操作时允许多个线程同时访问共享资源,在写操作时只允许一个线程访问
。
③
乐观锁
:
乐观锁是一种并发控制
机制,它假设多个线程之间很少会发生冲突,因此不会进行显式的加锁操作,而是通过一种乐观的方式进行并发访问。在乐观锁机制中,
线程在读取数据时,不会对数据进行加锁
,而是先读取数据的版本号,然后在对数据进行修改时,再次检查版本号是否发生变化。
如果版本号没有变化,说明其他线程没有修改过数据,当前线程可以进行修改,并更新版本号;如果版本号发生了变化,说明其他线程已经修改过数据,当前线程需要重新读取数据,然后再次尝试修改。
乐观锁相对于悲观锁来说,减少了对数据的加锁和解锁操作,因此在高并发场景下,乐观锁的性能通常比悲观锁更好
。
但是,由于乐观锁需要进行额外的版本号检查和数据重读操作,因此如果冲突较多,可能会导致性能下降。
4.并发工具类
①
ConcurrentHashMap
:
ConcurrentHashMap是Java中的一个线程安全的哈希表
,它用于在多线程环境下进行并发访问的操作。
它提供了一种高效的方式来存储和操作键值对
。
ConcurrentHashMap的主要作用是在多线程环境中提供安全的并发访问
。
它通过使用分段锁
(Segment)来提高并发情况下的性能。
每个Segment可以理解为一个独立的哈希表,不同的线程可以同时访问不同的Segment,从而减小了锁的粒度,提高了并发性能。
②
CountDownLatch
:
CountDownLatch是一个并发辅助类,它可以使一个或多个线程等待其他线程完成操作后再继续执行
。
在Java多线程中,CountDownLatch的作用是控制线程的执行顺序
。
当一个或多个线程需要等待其他线程完成某些操作后再继续执行时,可以使用CountDownLatch来实现。
③
CyclicBarrier
:
CyclicBarrier(循环屏障
)是Java多线程中的一个同步工具类,用于控制线程的执行顺序和并发同步。
它可以让一组线程在某个点上等待,直到所有线程都达到这个点,然后执行后续操作
。
④
Semaphore
:
在Java多线程中,Semaphore(信号量
)是一种用于控制同时访问特定资源(如线程数)的机制。
它可以用来限制同一时间内可以访问某个资源的线程数量
。
⑤
ExChanger
:
Exchanger是Java多线程中的一个工具类,用于线程之间的数据交换
。
它提供了一个同步点,在这个同步点上,两个线程可以交换彼此的数据。
具体来说,Exchanger有一个exchange方法,当一个线程调用exchange方法时,它会被阻塞,直到另一个线程也调用了exchange方法。
然后两个线程会交换彼此的数据,并继续执行。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!