注:Thread的 suspend() 方法容易导致死锁,所以Java不再推荐使用该方法来暂停线程的执行。
同步代码块
synchronized加在代码块上。示例:
synchronized(obj){
}
- 上述代码中obj就是同步监视器。
- 任何时刻都只能有一个线程可以获得同步监视器的锁定,当同步代码块执行完后,该线程就会释放对该同步监视器的锁定。
- 因为同步监视器的目的是:阻止两个线程对同一个共享资源进行并发访问。所以一般都用可能被并发访问的共享资源充当同步监视器。
同步方法
synchronized加在方法上。示例:
public synchronized void update(Object obj){
}
- 同步实例方法(非静态方法)的同步监视器,就是调用该方法的对象(即this)。要注意的是,是通过new创建的对象,而不是类。
关于synchronized类锁和对象锁的区别
Class A {
// ==>对象锁:普通实例方法默认同步监视器就是this,即调用该方法的对象
public synchronized methodA() {
}
public methodB() {
// ==>对象锁:this表示是对象锁
synchronized(this){
}
}
// ==>类锁
public static synchronized methodC() {
}
public methodD(){
// ==>类锁:A.class说明是类锁
synchronized(A.class){}
}
// 普通方法:任何情况下调用时,都不会发生竞争
public common(){
}
}
methodA,和methodB都是对当前对象加锁,即如果有两个线程同时访问同一个对象的methoA或methodB会发生竞争。如果两个线程访问的是不同对象的methodA和methodB则不会发生竞争。
methodC和methodD是对类加锁,即如果两个线程同时访问同一个对象的methodC和methodD会发生竞争,且两个线程同时访问不同对象的methodC和methodD是也会发生竞争。
- 如果一个线程访问methodA或methodB,另一个线程访问methodC或methodD,则这两个线程不会发生竞争。因为一个是类锁另一个是对象锁。类锁和对象锁是两个不一样的锁,控制着不同的区域,它们互不干扰。
5种类锁情况
同步块里加字符串的情况属于类锁,可能是因为String常量的唯一性导致的。具体我也不是很明白。
Class A {
// 普通字符串属性
private String val;
// 静态属性
private static Object staticObj;
// ==>类锁情况1:synchronized修饰静态方法
public static synchronized methodA() {
}
public methodB(){
// ==>类锁情况2:同步块里的对象是类
synchronized(A.class){}
}
public methodC(){
// ==>类锁情况3:同步块里的对象是字符串
synchronized("A"){}
}
public methodD(){
// ==>类锁情况4:同步块里的对象是静态属性
synchronized(staticObj){}
}
public methodE(){
// ==>类锁情况5:同步块里的对象是字符串属性
synchronized(val){}
}
}
同步锁(Lock)
- Lock是Java5提供的一个强大的线程同步机制(通过显示定义同步锁对象来实现同步)。
- Lock可以显示的加锁、解锁。每次只能有一个线程对Lock对象加锁,线程访问资源之前应先获得Lock对象。
- Lock有ReadLock(读锁)、WriteLock(写锁)、ReentrantLock(可重入锁),常用的就是ReentrantLock。
- 被相同锁保护的方法,可以相互调用。
- 示例:
public class Account
{
// 定义可重入锁对象:一个线程可以对已被加锁的ReentrantLock锁再次加锁。
private final ReentrantLock lock = new ReentrantLock();
public void draw(Object obj)
{
// ===加锁===
lock.lock();
try
{
//需要保证线程安全的代码
doSomethiing();
}
finally
{
// ===修改完成,释放锁===
lock.unlock();
}
}
}
Lock与synchronized 的区别
推荐阅读:https://www.cnblogs.com/lemon-flm/p/7880119.html,特别要注意和思考的是lock锁是类锁还是对象锁。
synchronized在发生异常时,会自动释放线程占有的锁;而Lock在发生异常时,如果没有主动通过unLock()释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
Lock可以让等待锁的线程响应中断去干别的事务,而synchronized却不行;使用synchronized时,等待的线程会一直阻塞直到拿到锁,不能够响应中断;
通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
在性能上,如果竞争资源不激烈,两者的性能差不多;而当竞争资源非常激烈时(即有大量线程同时竞争),Lock的性能要远远优于synchronized
- Lock可以提高多个线程读操作的效率:
当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作是不会发生冲突的。
如果采用synchronized来实现同步,当多个线程都只是读操作时,若其中一个线程在进行读操作,其他线程就只能等待。而 Lock使用读锁时,可以使得多个线程之间只有读操作时不会发生竞争。