1、五种方式
1.1 synchronized同步方法
使用synchronized关键字修饰的方法。java每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需获取内置锁,否则就会处于阻塞状态。
如:public synchronized void save(){}
注:当synchronized关键字修饰静态方法时,会锁住整个类
1.2 synchronized同步代码块
即有synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
如:synchronized(object){}
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
1.3 volatile
- volatile关键字为域变量的访问提供了一种免锁机制,
- 使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
- 因此每次使用该域就要重新计算,而不是使用寄存器中的值
- volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
注:用来对共享变量的访问进行同步,上一次写入操作的结果对下一次读取操作是肯定可见的。(在写入volatile变量值之后,CPU缓存中的内容会被写回内存;在读取volatile变量时,CPU缓存中的对应内容会被置为失效,重新从主存中进行读取),volatile不使用锁,性能优于synchronized关键词。
1.4 重入锁ReentrantLock
a. 概念及使用
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
ReentrantLock类的常用方法有:
- ReentrantLock(): 创建一个ReentrantLock实例
- lock(): 获得锁
- unlock(): 释放锁
注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用
b. 关于Lock对象和synchronized关键字的选择:
- 最好两个都不用,使用一种java.util.concurrent包提供的机制,能够帮助用户处理所有与锁相关的代码。
- 如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
- 如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁
1.5 局部变量
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
ThreadLocal类的常用方法
- ThreadLocal(): 创建一个线程本地变量
- get(): 返回此线程局部变量的当前线程副本中的值
- initialValue(): 返回此线程局部变量的当前线程的"初始值"
- set(T value): 将此线程局部变量的当前线程副本中的值设置为value
注:ThreadLocal与同步机制
- ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
- 前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式
2、synchronized
2.1 四种方式
- 方法声明时使用,线程获得的是成员锁.
- 对某一代码块使用,synchronized后跟括号,括号里是变量,线程获得的是成员锁.
- synchronized后面括号里是一对象,此时,线程获得的是对象锁.
- synchronized后面括号里是类,此时,线程获得的是对象锁.
2.2 使用特点
- synchronized关键定只是用来提供在多线程环境中对java共享资源的互斥访问。它保证了使用相同锁的多个线程的同步问题,
- synchronized关键定只能用在synchronized方法或者synchronized块中,不能对变量进行synchronized操作
- 当线程进入synchronized时,它必须获得相应的锁,而当它离开时,要释放对应的锁。锁会在线程完成同步或者是出现错误或异常时释放。
- 当Java Thread进行一个syncrhonized的Java方法时,会要求获得对象级别的锁;而当进入Static synchronized方法时,需要获得类级别的锁。
- Java Synchronized关键字是可以重入的。
- 如果java synchronized块同步的对象是null的话,JAVA会抛出NullPointerException异常。
- synchronized只能在同一个JVM上实现同步。
- 使用synchronized会造成performance的下降。
- Java synchronized block要优于synchronized method
- 在Java5之后,使用violate的变量都是原子的。
- synchronized不能用在构造函数中,它会造成编程的错误。
- synchronized不能用变量上
- Java类库Calendar和SimpleDataFormat不是线程安全的,所以需要另外的同步机制。
http://blog.csdn.net/php_boy/article/details/6778672
3、volatile
多线程线程并发操作时,保证内存中共享变量时时处于最新修改状态
3.1 volatile关键字的两层语义
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
- 禁止进行指令重排序。
什么是指令重排序,一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。
3.2 使用场景
synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。通常来说,使用volatile必须具备以下2个条件:
- 对变量的写操作不依赖于当前值
- 该变量没有包含在具有其他变量的不变式中
实际上,这些条件表明,可以被写入volatile变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
事实上,我的理解就是上面的2个条件需要保证操作是原子性操作,才能保证使用volatile关键字的程序在并发时能够正确执行。
4、ThreadLocal
4.1 使用特点
ThreadLocal使用场合主要解决多线程中数据因并发产生不一致问题。
ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。
4.2 使用步骤
- 在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。
- 在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。
- 在ThreadDemo类的run()方法中,通过getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。
5、volatile与synchronized
- volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法.
- volatile仅能实现变量的修改可见性,但不具备原子特性;而synchronized则可以保证变量的修改可见性和原子性.
- volatile不会造成线程的阻塞;而synchronized可能会造成线程的阻塞.
- volatile标记的变量不会被编译器优化;而synchronized标记的变量可以被编译器优化.
总结:volatile本质是在告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取。可以实现synchronized的部分效果,但当n=n+1,n++等时,volatile关键字将失效,不能起到像synchronized一样的线程同步的效果。
6、ReentrantLock与synchronized
6.1 异同点
相同:ReentrantLock提供了synchronized类似的功能和内存语义。
不同:
- ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,用ReentrantLock更合适,ReentrantLock还提供了Condition,对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition实例,所以更有扩展性。
- ReentrantLock必须在finally中释放锁,否则后果很严重,编码角度来说使用synchronized更加简单,不容易遗漏或者出错。
- ReentrantLock 的性能比synchronized会好点。
- ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。
6.2 使用特点:
- Lock的某些方法可以决定多长时间内尝试获取锁,如果获取不到就抛异常,这样就可以一定程度上减轻死锁的可能性。
- 如果锁被另一个线程占据了,synchronized只会一直等待,很容易错序死锁
- synchronized的话,锁的范围是整个方法或synchronized块部分;而Lock因为是方法调用,可以跨方法,灵活性更大
- 便于测试,单元测试时,可以模拟Lock,确定是否获得了锁,而synchronized就没办法了
6.3 ReentrantLock比synchronized 强大在哪儿?
简单说:
1、ReentrantLock可以实现fair lock
1 public ReentrantLock(boolean fair) { 2 sync = (fair)? new FairSync() : new NonfairSync(); 3 }
所谓fair lock就是看获得锁的顺序是不是和申请锁的时间的顺序是一致的
2、ReentrantLock支持中断处理
1 public final void acquireInterruptibly(int arg) throws InterruptedException { 2 if (Thread.interrupted()) 3 throw new InterruptedException(); 4 if (!tryAcquire(arg)) 5 doAcquireInterruptibly(arg); 6 }
就是说那些持有锁的线程一直不释放,正在等待的线程可以放弃等待。
3、ReentrantLock可以和condition结合使用
1 public boolean hasWaiters(Condition condition) { 2 if (condition == null) 3 throw new NullPointerException(); 4 if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) 5 throw new IllegalArgumentException("not owner"); 6 return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject) condition); 7 } 8 9 public int getWaitQueueLength(Condition condition) { 10 if (condition == null) 11 throw new NullPointerException(); 12 if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) 13 throw new IllegalArgumentException("not owner"); 14 return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject) condition); 15 }
7、ThreadLocal与synchronized:
- synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
- Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。
http://www.2cto.com/kf/201408/324061.html
http://www.cnblogs.com/dolphin0520/p/3920373.html
http://my.oschina.net/songhongxu/blog/197925