首先引入一个概念,什么是线程安全?
Brian大师定义为“如果不用考虑这些线程,在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的".
在java语言中,各种操作共享的数据一共有5类
1 不可变类 string,final修饰的变量,最简单粗暴,包装器类型。
2 绝对线程安全 注意这个几乎没有,就算是api表明自己是安全的 比如vector,他的所有操作,crud,都加上了synchronized,只是进行一个操作,当然没有问题,但是如果用两个方法,就有可能出现问题,这个时候,还是需要把这两个方法,再加上同步块,所以线程安全的类还是不能完全解决问题,详情点击点击打开链接
3 相对安全的类 这就是前边说的api表明为线程安全的类了。例如vector,hashtable等
4 线程兼容 就是普通类,我们可以采用方法使其线程安全 例如arraylist,hashmap等
5 线程对立,就是采取什么方式也解决不了同步安全的问题。比如Thread的suspend,resume,怎么操作都有可能发生死锁,一个去加锁,一个去恢复。
下面介绍线程安全的方法
1 互斥同步 常见的方法,阻塞式,包括临界区,互斥量,信号量 ,基本的方法就是synchronized,其实它编译之后,会在同步代码的前后分别形成 monitorenter和monitorexit,这两个字节码指令,这两个指令需要引用类型的参数,来指明加锁,解锁的对象,或者是修饰方法。除了reentrantlock类,lock和unlock配合try,catch使用。并且还可以等待可中断,可实现公平锁,以及锁可以绑定多个条件,等待可中断的意思,就是有中断时间很长的,线程可以选择去干其他事情,公平锁就是谁先来谁最早获得(前者是非公平的),锁绑定更多个条件,通过多次调用condition。其实前者后者现在的效率差不多,因为对synchronized进行了很多优化,所以优先选择用它。
2 非阻塞式的同步 主要是由于硬件指令集的发展,对于互斥同步,带来的性能问题,有了基于冲突检测的的乐观并发策略,也就是先进行操作,如果没有其他线程争用共享数,操作成功,冲突后,会采取补偿措施。所以操作和冲突检测这两个步骤需要硬件来保证原子性。像是Atomicinteger 就是这种,incrementandget方法时,会一直检测是否有冲突,有冲突就无限循环继续搞,但是有可能出现aba模式,就是先变成啊,后来变成吧,再变成a,会引起他认为没有改变。
3 无同步方案
a: 可重入代码,就是超级安全,怎么输入都是输出一样的值,不依赖与堆里的数据
b:线程本地存贮
查看共享的数据代码能否在一个线程中执行。应用不多,web服务器中一个请求对应一个服务器线程就是这种,java中threadlocal就是这种,threadlocal的具体请点击
下面介绍锁优化的方法
1 自旋锁
挂起线程和回复线程需要转入内核态中完成,给操作系统的并发性带来很大压力。如果一个物理机器有一个以上的处理器,能让两个或以上的线程同时并发执行,我们就可以,让后面的请求锁的那个线程稍等一下,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会被释放,需要让一个线程,忙循环,这就叫做自旋。
2 锁消除
如果一段代码已经被同步,但是检测很安全,就会去除锁。
3锁粗化
好几条同步代码块,可以整合到一块
4 轻量级锁
5偏向锁
最后总结一下线程同步的方法。
1 :volatile (不懂看我的另一篇博客)
2 :synchronized
3 :reentrantlock
4 :threadlocal( 这个就是线程本地存贮,就是对于一个变量每个线程含有一个副本,但是要保证不是临界资源,就是一个线程自己用完了销毁就是,比如数据库连接资源,以及hibernate的session资源。
5 :wait,notify。
以上知识整理来自深入理解java虚拟机