Java的两种同步方式Synchronized和ReentrantLock的区别

- Synchronized
Synchronized经过过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。
Synchronized的用法如下:


public class SynDemo{   
    public static void main(String[] arg){  
        Runnable t1=new MyThread();  
        new Thread(t1,"t1").start();  
        new Thread(t1,"t2").start();  
    }  
}  
class MyThread implements Runnable {  
    @Override  
    public void run() {  
        synchronized (this) {  
            for(int i=0;i<10;i++)  
                System.out.println(Thread.currentThread().getName()+":"+i);  
        }        
    }  
}  

指令字节码
在这里插入图片描述
- ReentrantLock
ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:
1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。
2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定多个对象。
ReentrantLock的用法如下:

public class SynDemo{  
    public static void main(String[] arg){  
        Runnable t1=new MyThread();  
        new Thread(t1,"t1").start();  
        new Thread(t1,"t2").start();  
    }  
}  
class MyThread implements Runnable {    
    private Lock lock=new ReentrantLock();  
    public void run() {  
            lock.lock();  
            try{  
                for(int i=0;i<5;i++)  
                    System.out.println(Thread.currentThread().getName()+":"+i);  
            }finally{  
                lock.unlock();  
            }  
    }  
}  

两者的区别:

区别一:API层面

synchronized既可以修饰方法,也可以修饰代码块。
synchronized修饰代码块时,包含两部分:锁对象的引用和这个锁保护的代码块。

区别二:等待可中断

等待可中断是指当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。可等待特性对处理执行时间非常长的同步快很有帮助。
具体来说,假如业务代码中有两个线程,Thread1 Thread2。假设 Thread1 获取了对象object的锁,Thread2将等待Thread1释放object的锁。

  • 使用synchronized。如果Thread1不释放,Thread2将一直等待,不能被中断。synchronized也可以说是Java提供的原子性内置锁机制。内部锁扮演了互斥锁(mutual exclusion lock ,mutex)的角色,一个线程引用锁的时候,别的线程阻塞等待。
  • 使用ReentrantLock。如果Thread1不释放,Thread2等待了很长时间以后,可以中断等待,转而去做别的事情。

区别三:公平锁

公平锁是指多个线程在等待同一个锁时,必须按照申请的时间顺序来依次获得锁;而非公平锁则不能保证这一点。非公平锁在锁被释放时,任何一个等待锁的线程都有机会获得锁。
synchronized的锁是非公平锁,ReentrantLock默认情况下也是非公平锁,但可以通过带布尔值的构造函数要求使用公平锁。

区别四:锁绑定多个条件

ReentrantLock可以同时绑定多个Condition对象,只需多次调用newCondition方法即可。
synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件。但如果要和多于一个的条件关联的时候,就不得不额外添加一个锁。

区别五:性能

JDK1.5,synchronized还有很大的优化余地。JDK 1.6 中加入了很多针对锁的优化措施,synchronized与ReentrantLock性能方面基本持平。虚拟机在未来的改进中更偏向于原生的synchronized。

关于synchronized关键字

(1)Java中每个对象都有一个锁(lock)或者叫监视器(monitor)。
(2)ReentrantLock和Synchronized持有的对象监视器不同。
(3)如果某个synchronized方法是static的,那么当线程调用该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在对象对应的Class对象,因为Java中不管一个类有多少对象,这些对象都会对应唯一一个Class对象。因此当线程分别访问一个类的两个对象的两个static,synchronized方法时,是顺序执行的,亦即一个线程先执行完毕之后,另一个才开始执行。
(4)synchronized 方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行synchronized方法;synchronized块则是一种细粒度的并发控制。只会将块中代码同步,位于方法内,synchronized块之外的代码是可以被多个线程同时访问的。
(5)synchronized关键字经过编译之后,会在同步块的前后分别形成monitorenter和monitorexit两个字节码指令,操作对象均为锁的计数器。
(6)相同点:都是可重入的。可重入指同一个线程多次试图获取它所占的锁,请求会成功。当释放的时候,直到重入次数清零,锁才释放。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值