- 不推荐覆写start方法
- 启动线程前stop方法是不可靠的
- 不使用stop方法停止线程
有以下三个问题:
1、stop方法是过时的
2、stop方法会导致代码逻辑不完整
3、stop方法会破坏原子逻辑
如果期望终止一个正在运行的线程,则不能使用已经过时的stop方法,需要自行编码实现,如此即可保证原子逻辑不被破坏,代码逻辑不会出现异常。当然,如果我们使用的是线程池(比如ThreadPoolExecutor类),那么可以通过shutdown方法逐步关闭池中的线程,他采用的是比较温和、安全的关闭线程方法,完全不会产生类似stop方法的弊端。
- 线程优先级只使用三个等级
线程优先级推荐使用MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY三个级别,不建议使用其他7个数字。
- 使用线程异常处理器提升系统可靠性
Java 1.5版本以后在Thread类中增加了setUncaughtExceptionHandler方法,实现了线程异常的捕捉和处理。
实际环境中应用,需要注意以下三个方面:
1、共享资源锁定
2、脏数据引起系统逻辑混乱
3、内存溢出
- volatile不能保证数据同步
注意:volatile不能宝恒数据是同步的,只能保证线程能够获得最新值。
- 异步运算考虑使用Callable接口
从Java 1.5开始引入了一个新的接口Callable,他类似于Runable接口,实现他就可以实现多线程任务。
此类异步计算的好处是:
1、尽可能多地占用系统资源,提供快速运算。
2、可以监控线程执行的情况,比如是否执行完毕、是否有返回值、是否有异常等。
3、可以为用户提供更好的支持,比如例子中的运算进度等。
- 优先选择线程池
一个线程的运行时间分为三部分:T1为线程启动时间,T2为线程体的运行时间,T3为线程销毁时间,如果一个线程不能被重复使用,每次创建一个线程都需要经过启动、运行、销毁这三个过程,那么这势必会增大系统的响应时间,有没有更好的办法降低线程的运行时间呢?
T2是无法避免的,只有通过优化代码来实现降低运行时间。T1和T2都可以通过线程池(Thread Pool)来缩减时间,比如在容器(或系统)启动时,创建足够多的线程,当容器(或系统)需要时直接从线程池中获得线程,运算出结果,再把线程返回到线程池中。
线程池的创建过程:创建一个阻塞队列以容纳任务,在第一次执行任务时创建足够多的线程(不超过许可线程数),并处理任务,之后每个工作线程自行从任务队列中获得任务,直到任务队列中的任务数量为0为止,此时线程将处于等待状态,一旦有任务再加入到队列中,即唤醒工作线程进行处理,实现线程的可复用性。
使用线程减少的是线程的创建和销毁时间。
- 适时选择不同的线程池来实现、
Java的线程池实现从最根本上来说只有两个:ThreadPoolExecutor类和ScheduledThreadPoolExecutor类,这两个类还是父子关系,但是Java为了简化并行计算,还提供了一个Executors的静态类,他可以直接生成多种不同的线程池执行器,比如单线程执行器、带缓冲功能的执行器等,但归根结底还是使ThreadPoolExecutor类或ScheduledThreadPoolExecutor类的封装性。
newSingleThreadExecutor、newCachedThreadPool、newFixedThreadPool是线程的简化版,而ThreadPoolExecutor则是旗舰版——简化版更容易操作,需要了解的知识相对少些,方便实用,而且旗舰版功能齐全,适用面广,但难于驾驭。
- Lock与synchronized是不一样的
显示锁和内部锁的不同之处:
1、Lock支持更细粒度的锁控制
2、Lock是无阻塞锁,synchronized是阻塞锁
3、Lock可实现公平锁,synchronized只能是非公平锁
4、Lock是代码级的,synchronized是JVM级的
根据实际情况考虑:灵活、强大则选择Lock,快捷、安全则选择synchronized。
- 预防线程死锁
在我们Java多线程并发编程中,死锁很难避免,也不容易预防,对付他的最好办法是测试:提高测试覆盖率,建立有效地边界测试,加强资源监控,这些方法能使死锁无处遁形,即使发生了死锁现象也能迅速查找到原因,提高系统的可靠性。
- 适当设置阻塞队列长度
- 使用CountDownLatch协调子线程
- CyclicBarrier让多线程齐步走
在多线程编程中,两个线程独立运行,在没有线程间通信的情况下,如何解决两个线程汇集在同一远点的问题。Java提供了CyclicBarrier(关卡,也有翻译为栅栏)工具类来实现。