前言
一段代码对共享资源进行读写操作的代码块称为临界区
1.Synchronized
Synchronized方法保证了临界区代码的原子性,即这段代码不可被分割,不会因为上下文切换而被打断
1.1 方法上的Synchronized
JAVA面对对象编程,所有方法必有其附属的类,所以加在方法上的synchroniezed如下图所示等同于用自己所在的类加锁。
public class Test01{
public synchronized void test()
{
}
public void test1()
{
synchronized(this){
}
}
}
2.线程安全分析
成员变量和静态变量如果被多个线程共享且读写操作混杂则它们是不安全的
局部变量若只是在方法内被初始化,即其生命周期跳不出方法,那么它是安全的,但是很多局部变量引用了其他的东西比如:比如List a =new ArrayList(),局部变量a引用了创建在堆中的变量,该变量若能在方法内被清除(释放其在堆中的空间),那么它就是安全的。
2.1 局部变量的暴露引用问题
当有子类继承的时候,method2和method3都是public对子类暴露,子类可以重写这两个方法,所以我初始化ThreadSafe t=new ThreadSafeSubClass(),调用t.methode1() 方法,该方法中运行的就是子类的method3方法,该方法创建了一个线程,且因为list是对堆变量的引用,导致三个方法中的list是一个共享变量,出现线程安全问题。所以把method2和method3设置为private能够保护不被重写(我试验过了,子类不能重写private方法,当子类调用method1时,因为这是父类的方法,所以里面的method2和method3也必须调用父类的方法,如果子类重写了就调用子类重写,如果没有重写,这些方法自动失效不会再调用了)
3.常见线程安全类
String, Integer, StringBuffer, Random, Vector, HashTable, java.util.concurrent包下的类都是线程安全的
3.1方法原子性
这些类中的方法每一个都能保证原子性,什么意思呢:
比如Integer中的 int a=c+b方法,这个方法别看只有一行代码,但是它由几行字节执行码组成(比如有四行字节码),这些字节码不可能同时运行完成,还是会按照顺序执行,如果这时因为上下文切换到其他线程了 ,只执行了两行字节码,其他线程再对这四行字节码想要操作的共享变量进行操作时,就会产生线程安全问题,而这几个类的每一个方法中字节码都会一次性执行完而不会被分割。
但是两个方法在一起就不存在原子性了,毕竟只能保证每个方法内部的原子性,两个方法在一起的原子性是不能保证的。
3.2不可变类
这几个类都是不可变的,虽然像substring,replace等方法都能改变String对象,但是这些方法会先复制一份,然后对这份复制的进行操作。
当一个对象中的属性可以被共享改变时,那么它就是不安全的,比如:
userDao这个对象中,只有一个方法,每个线程拥有该对象时,只有一个功能那就是调用那个update方法,该方法中的变量如Connection对象,