Java语言中各种操作共享的数据分为以下5类:不可变,绝对线程安全,相对线程安全,线程兼容和线程对立
线程安全的实现方法:
1.互斥同步:同步是指在多个线程并发访问共享数据时,保证共享数据在同一时刻只被一个(或者是一些,使用信号量的时候)线程使用.而互斥是实现同步的一种手段,临界区,互斥量和信号量都是主要的互斥实现方式.因此,在这4个字里面,互斥是因,同步是果;互斥是方法,同步是目的.
最基本的互斥同步手段就是synchronized关键字
还可以使用java.util.concurrent包中的重入锁(ReentrantLock)来实现同步.ReentrantLock增加了一些高级功能:
a.等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,可中断特性对处理执行时间非常长的同步块又帮助
b.可实现公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁:而非公平锁则不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁.synchronized中的锁是非公平的,ReentrantLock默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁
c.锁可以绑定多个条件:指一个ReentrantLock对象可以同时绑定多个Condition对象,而在synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外地添加一个锁,而ReentrantLock则无须这样做,只需要多次调用newCondition()方法即可
2.非阻塞同步:互斥同步的最主要问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步.从处理问题的方式上说,互斥同步属于一种悲观的并发策略
3.无同步方案
锁优化:
1.自旋锁和自适应自旋:如果锁被占用的时间很短,自旋等待的效果就会非常好,反之,如果锁被占用的时间很长,那么自旋的线程只会白白消耗处理器资源,会带来性能上的浪费.自旋次数默认是10次,可以使用参数-XX:PreBlockSpin来更改
自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定.如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环.另外,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源.
2.锁消除:虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除.锁消除的主要判定依据来源于逃逸分析的数据支持
3.锁粗化:本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗
4.轻量级锁
5.偏向锁