一、线程安全总结梳理
要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的访问。一个对象是否需要是线程安全的,取决于它是否被多个线程访问。要使得对象是线程安全的,需要采用同步机制来协同对象可变状态的访问。当多个线程访问某个状态变量并且其中一个线程执行写入操作时,必须采用同步机制来协同这些线程对变量的访问。
java中主要同步机制是关键字synchronized,同时还包括volatile类型的变量,显示所lock和原子变量。
如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现问题,有三种修复方法如下:
1.不再线程之间共享变量
2.将状态变量改为不可变的变量
3.在访问状态变量时使用同步。
在任何情况中,只有当类中仅包含自己的状态时,线程安全类才是有意义的。线程安全性是一个在代码上使用的术语,但它只是与状态相关的,因此只能应用于封装其状态的整个代码,这可能是一个对象,也可能是整个程序。
线程安全的定义:
在多个线程中调用,并且在线程之间不会出现错误交互;
同时被多个线程调用,而调用者无需执行额外的动作;
当多个线程访问某个类的时候,这个类始终都i能表现出正确的行为,那么这个类就可以说是线程安全的。
合适的一个定义: 当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何的额外的同步和协同,这个类都i将表现出正确的行为,那么就称这个类是线程安全的。
无状态类一定是线程安全的,因为线程安全只是与状态相关的,所以无状态类一定是线程安全的。当在无状态的类中添加一个完全由线程安全的对象管理的状态时,这个类仍然是线程安全的,但是,当此种变量有一个变为多个时就需要加锁机制来实现同步了,因为两个线程安全类管理的的状态,单个时是原子的,两个时就可能变成了复合操作,竞态条件成为了可能。
竞态条件:在并发程序中,由于不恰当的执行时序而出现不正确的结果是一种非常重要的情况,这种情况就叫做竞态条件。典型的如“先检查后执行”代码
在竞态条件中,往往包含一组需要以原子方式执行的操作,要避免竞态条件问题,就必须在某个线程修改该变量时,通过某种方式防止其他线程使用这个变量,从而确保其他线程只能在修改操作完成之前或者之后读取和修改状态,而不是在修改状态的过程中。
并非所有数据都需要锁的保护,只有被多个线程同时访问的可变数据才需要通过锁来保护。
如果将每个方法都作为同步方法可能会导致活跃性或性能问题。通常,通过缩小同步代码块的作用范围,我们很容易做得到既确保类如servlet的并发行,同时又维护线程安全线,要确保同步代码块不要过小,并且不要将本应是原子的操作拆分到多个同步代码块中,应该尽量将不影响共享状态且执行时间较长的操作从同步代码块中分离出去,从而在这些操作的执行过程中,其他线程可以访问共享状态。
如果持有锁时间过长,那么就会带来活跃性或性能问题,如当执行时间较长的计算或者可能无法快速完成的操作时(如网络IO或者控制台IO),一定不要持有锁。