java并发编程初探

**本文是在阅读java并发编程实战时,随手记录的笔记,可能会有点凌乱,欢迎各位指正~**

1、共享对象

1.1 可见性

​ 如果不用volatile保护变量,共享变量可能会获取到过期值。但是如果是64位的变量值如double、long,则可能会获取一个线程的高八位,和另一个线程的低八位,导致数据错误异常。此处的八位其实是2^8个字节位,也就是前32位,后32位。

​ 注意用加锁的方式来保护变量,亦可以达到volatile的效果,甚至更强,保护变量在其他线程中可见,否则很有可能在其他线程不可见。

1.2 this引用逸出

​ 在构造函数中进行发布对象,由于发布的内部对象含有对外部构造函数构造的对象this的隐式指向,从而导致构造函数所在的对象this引用逸出,产生安全问题,即可能构造尚未完成,但是发布出去的内部对象被他人使用该this引用,使用了未构造完成的对象。

​ 防止this引用逸出,可以通过私有化构造函数,提供共有的工厂方法来完成须在构造函数中的初始化工作,从而防止在构造未结束之前引用逸出。

1.3 Final关键字的使用

​ final关键字应尽可能地使用,因为final可以保证不可变对象(所有属性或变量均是不可变的)的线程安全性,可对其他线程的可见性,还能保证对象正确初始化完毕后对其他线程的可见性。

2、组合对象

2.1 错误的锁

并不是给方法或代码片段加了锁就可以保证并发性的安全问题,如果加锁的对象不是同一个的话,并不能保证并发性安全。

​ 例如ConcurrentHashMap,对它的加锁与它自身的内部分段锁不是同一个锁,所以不能保证它并发下的独占式访问。

3、构建块

3.1 CyclicBarrier 循环栅栏

通过构造CyclicBarrier时,传入数量和到达栅栏时的最后一个进程的执行事件,完成一个栅栏的构建,内部通过barrier.await()来等待所有线程到达栅栏,注意可循环使用,每次await()即可。

Cyclicbarrier  barrier  = new CyclicBarrier(threadNum,ThreadDeomo);
barrier.await();

3.2 Semaphore 信号量

初始化时,需要指令信号量容量, 可以阻塞至信号量被消费完,空余出剩余容量,继续执行,类似行车通道的栅栏。

Semaphore semaphore = new Semaphore(2);
//使用信号量
semaphore.acquire();
//释放信号量
semaphore.release();

4、任务的取消和中断

4.1 interrupt

​ thread调用interrupt()后,会向线程中的阻塞方法发送一个中断请求,如果线程被中断,会向外抛出interruptedException,并清除中断标志,即通过thread.isInterrupted()不能获取中断标志了,这就需要我们手动调用thread.interrupt()来恢复中断标志。

​ 如果线程运行结束,进入terminated状态,也会清除中断标志。

​ interrupt其实并不是直接中断线程,而是将当前线程的interrupt状态置位true,如果不是阻塞方法实现了这个标志位校验,则需要我们手动去获取并处理该标志位。

​ 不是所有的阻塞方法都能够提前抛出InterruptedException来实现对中断的响应,如果是类似I/O等待或者内部锁等待的阻塞,中断仅仅只能改变线程的中断状态标志,并不能实际中断线程。

4.2 线程池

​ 线程池ThreadPoolExecutor是通过创建线程,然后通过start启动,每次实际调用的run方法实际是从blockingQueue中获取的工作任务,实现线程复用。

​ 线程池参数有核心线程数、最大线程数、有效存活时间、缓冲队列长度,线程工厂、拒绝策略等,线程池如果不shutdown,将会一直运行,shutdown其实是通过interrupt机制,传递线程终止状态信息,结束线程的while循环实现的。

​ 线程池的停止有两种方法,shutdown() shutdownNow() 区别在于停止时,前者会继续运行等待队列的任务,后者会返回等待队列的任务并向运行中的线程发送intrrupted指令。调用线程池停止后,可以通过调用awaittermination() 来阻塞返回线程池是否关闭成功。

4.3 未捕获异常

​ UncaughtExceptionHandle接口的实现类可以通过Thread.setUncaughtExceptionHandler()来注入未捕获异常的处理,然后再停止线程。

5、JVM关闭

5.1 jvm退出

​ jvm的关闭可以使用System.exit(0) 或者Runtime.getRuntime().halt()来关闭,区别在于前者是正常关闭,会执行runtime中注册的shutdownHook子线程,后者是强制关闭,类似于kill命令,不会执行hook线程。

jvm在正常退出时,会检查是否有精灵线程之外的运行中线程,精灵线程不会阻碍jvm关闭。

5.2 运行系统命令

​ runtime可以通过Runtime,getRuntime().exec(“msg /server:10.240.0.105 * 这是一条测试命令”); 来运行系统命令,完成调用外部应用的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值