线程安全问题一般出现在并发度比较高的环境中,如存钱取钱问题等,解决该类问题一般有几种办法。
1.隐式锁synchronized
synchronized是非常常见的解决线程安全问题的一种方案,它有两种用法:
1.代码块:
synchronized (锁对象){ }
锁对象:它决定了该锁的涉及范围,对于实例对象通常为“this”,对于静态对象通常为“.class”,通俗来说它决定了影响线程的范围。
2.方法签名:
public static synchronized void lock_case(){ }
锁对象:默认跟随方法是否是静态方法。
2. 显式锁reentrantLock
Lock reentrantLock = new ReentrantLock();
锁对象:跟随所属的类
3.线程池
为什么要线程池?
为了线程复用,节省cpu开销。
如何创建线程池?
线程池是实现ExecutorService接口,有两个方法
1.使用Executors工具类提供的静态方法
注意:不允许使用该方法,原因:
会有资源耗尽的风险,
single和fixed中任务等待队列容量为Integer的最大值,可能导致堆积大量任务请求;
cached和scheduled中允许创建最大线程数量为Integer的最大值,可能导致创建大量线程;
2.new ThreadPoolExecutor()
如何配置参数核心线程数和最大线程数?
主要看业务处理的类型是CPU密集型还是IO密集型,CPU密集型包括大量CPU调度如计算,此时配置的核心线程数量最好和CPU核心数相等;
IO密集型针对大量的IO操作如网络通信和磁盘读写,由于IO操作需要系统调用会花费大量时间,所以此时需要更多的线程来充分利用CPU等待的时间
总之就是看CPU的利用率,如果低就增加线程池大小,如果CPU利用率高就降低以减少线程竞争。
线程池拒绝策略:
1.拒绝并抛异常
2.拒绝不理
3.接受并抛弃最开始进入的任务
4.提交给主线程执行
线程池的方法:
1.执行任务:execute(runnable任务)、submit(callable任务)
2.shutdown():关闭,执行未完成的任务
3.shutdownNow():返回未执行的任务