您的位置:首页 > 编程语言 > Java开发

Java高级之线程同步

2016-06-29 21:50 253 查看
       本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!

关于实现多线程的意义,“从业四年看并发”一文已经讲述,而本篇主要讲一下常用的设计模式和对象介绍,关于底层,请查看“Java高级之内存模型分析”。
通常情况下,狭义上来说,实现了变量或对象的原子性,即可以实现线程安全。什么叫线程的原子性,即执行read-load-assign-use-store-write这6个步骤,其一执行,则其他必执行完,因此来保证数据的同步。一般像long和double均为64位,会被拆分成2个32位执行,原则上会得到一个非64位的值,但实际上基本不会发生,也就是说它俩不具备标准的原子性。
synchronized可以说是唯一能实现标准同步的做法,底层使用monitorenter和monitorexit高级指令来实现同步块的原子性
常用的线程同步对象一般有这么几种,
1、使用synchronzied锁方法,指该方法操作完,其他线程才能操作
2、使用synchronzied锁对象,指该线程操作完此对象,其他线程才能操作
3、Lock,如ReentantLock,跟synchronized功能类似,不过它是使用lock+unlock+try/catch/finally实现,而前者使用底层指令,区别在于ReentrantLock拥有3种特性:1、等待可中断,在对象长期持有锁的情况话,线程可以放弃等待;2、公平锁,按时间顺序来获得锁,而synchronized不同,它是非公平锁,谁抢到是谁的;3、可绑定多个条件,通过Condition对象来设置,这样可以让线程在合适的时候放弃等待或执行其他操作;在线程数量比较多的时候,synchronized具有明显优势。
4、wait+notify,阻塞线程,等待通知后执行,通常用于生产者-消费者模式
5、sleep,轻量级的同步方法,在其他线程将数据准备好之后,再执行本线程。
6、volatile,非标准同步方法,谨慎使用;主要用于保证变量对所有线程的可见性和禁止指令重排序。前者特性表现在访问变量时,每次都会从内存中重读,因此主要应用在关闭多线程的执行上来;后者指保证代码顺序执行,而非变量前面有个线程未执行完,马上就执行到此变量。
        7、Semaphore,信号量,包含创建、等待、试图等待、释放、销毁几种,从生命周期即可看出,它是个线程同步的工具,跟锁是个线程互斥的工具不同,为互斥量为0表示锁定,大于0表示可使用(少数情况允许多条线程同时访问资源)。这也是互斥量和信号量的区别。

不同线程拥有不同的工作内存,像缓存机制一样,存在于处理器-工作内存-主内存系统中。变量均存在主内存中,工作内存存放主内存副本;不同线程不能直接访问对方的工作内存,只能通过主内存当桥梁来操作。
线程安全的实现方法有以下3种
1、互斥同步,即同一时间仅有一条线程可以使用共享数据,是一种阻塞同步,使用wait+notify的方式
2、非阻塞同步,乐观想法-同一时间仅有一条线程对数据操作,但做好补偿策略,防止多线程同时对数据操作,采用不断尝试,直到成功的策略;这可能出现问题,但在相当程度上,会使用并发速度高出几个量级;共享数据最好使用具有原子型的。
3、无同步,数据不会被共享的则无需同步或者仅在当前线程中被使用

关于锁,研究的细致一点,发现线程切换也是耗时的,那么锁可以做哪些优化呢?
1、自旋锁和自适应锁,1.6之后默认开启自旋锁,指出现线程等待,则停留一段时间,避免线程切换;如果停留时间非常短则自适应,特别长,虚拟机设置自旋10次则退出;自适应锁是智能的自旋锁,智能在于能跳过某个经常需要等待的线程。
2、锁消除,如上无同步状态,则无需加锁,重点在于避免程序自动同步,如StringBuffer的append方法,如无必要,可以使用StringBuilder代替。
3、锁粗化,如果几个方法均需加锁,而且在顺序上或时间上有次序,则可以共同放一起加锁,避免总是加锁解锁
4、轻量级锁,本意是减少重量级锁互斥产生的性能的消耗,无竞争状态下使用,加锁和解锁均通过CAS操作
5、偏向锁,解决轻量级锁在无竞争情况下,把整个同步都去掉,连CAS操作也不要了

大概关于线程同步的问题就先说这么多,回头有机会再补充。
介绍一种高效的单例模式,避免懒加载出现的一个单例仅能被一条线程持有的问题。
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
Singleton ins = new Singleton();
/* ensure object has been fully constructed before assigned to instance
* rule: in a thread, construction of an object -> access to the object.
*/
instance = ins;
}
}
}
return instance;
这样每条线程均可以直接持有此单例,仅在instance为空时将其初始化即可。
而sychronized关键字底层实现是moniterenter和moniterexit前者是将当前线程挂起,切换到内核层进行处理,处理完再唤起当前线程。
1.5引入CountDownLatch和CyclicBarrier,前者等待所有线程执行完毕再继续执行,后者等待到某个状态一起执行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: