Synchronized和ReentrantLock的作用都可以在多线程中对于共同访问的资源进行加锁,它们的区别在这这里记录下。
1.锁的实现
ReentrantLock是JDK实现的 Synchronized 是JVM实现;
前者可以直接看到源码,后者实现难以看到。
2.性能的区别
在JDK1.6之前,Synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。
3.功能的区别
便利性:很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁,锁的细粒度和灵活度上ReenTrantLock则要优于Synchronized。
4.是否可中断等待
具体说来,假如业务代码中有两个线程,Thread1 Thread2,.假设Thread1获取了对象object的锁,Thread2将等待Thread1释放object的锁。
• 使用synchronized。如果Thread1不释放,Thread2将一直等待,不能被中断。synchronized也可以说是Java提供的原子性内置锁机制。内部锁扮演了互斥锁的角色,一个线程引用锁的时候,别的线程阻塞等待。
• 使用ReentrantLock。如果Thread1不释放,Thread2等待了很长时间以后,可以选择中断等待,转而去做别的事情。
举个例子:
使用synchronized不可被中断。
Demo1:
public class Demo1{
private Object lock;
public Demo1() {
lock = this;
}
public void write() {
synchronized (lock) {
long startTime = System.currentTimeMillis();
System.out.println("往这个buffer写入数据...");
for(;;) {
if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE) {
break;
}
}
System.out.println("终于写完了");
}
}
public void read() {
synchronized (lock) {
System.out.println("从这个buff读数据");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Demo1 demo= new Demo1();
final Writer writer = new Writer(demo);
final Reader reader = new Reader(demo);
//启动线程
writer.start();
reader.start();
//尝试启动一个线程去中断reader线程
new Thread(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
for(;;) {
//等待五秒钟去中断
if (System.currentTimeMillis() - start > 5000) {
System.out.println("不等了,尝试中断");
reader.interrupt();
break;
}
}
}
}).start();
}
}
Writer类:
public class Writer extends Thread {
private Demo1 demo;
public Writer(Demo1 demo) {
this.demo= demo;
}
public void run() {
demo.write();
}
}
Read类:
public class Reader extends Thread {
private Demo1 demo;
public Reader(Demo1 demo) {
this.demo= demo;
}
public void run() {
demo.read();//可以收到中断的异常,从而有效退出
System.out.println("读结束");
}
}
运行结结果:
往这个buffer写入数据...
不等了,尝试中断
可以看到调用interrupt()方法时,并不会中断等待的线程。
使用ReentrantLock可以尝试中断。
Demo2类:
public class Demo2{
private Lock lock = new ReentrantLock();
public void write() {
lock.lock();
try {
long start = System.currentTimeMillis();
System.out.println("开始往这个buff写入数据...");
//模拟要处理很长时间
for (;;) {
if (System.currentTimeMillis() - start > Integer.MAX_VALUE) {
break;
}
}
System.out.println("终于写完了");
} finally {
lock.unlock();
}
}
public void read() throws InterruptedException {
lock.lockInterruptibly(); //获得可中断锁
try {
System.out.println("从这个buff读数据");
} finally {
lock.unlock();
}
}
public static void main(String args[]) {
Demo2 demo = new Demo2();
final Writer2 writer = new Writer2(demo);
final Reader2 reader = new Reader2(demo);
writer.start();
reader.start();
new Thread(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
for (;;) {
if (System.currentTimeMillis()
- start > 5000) {
System.out.println("不等了,尝试中断");
reader.interrupt(); //此处中断读操作
break;
}
}
}
}).start();
}
}
Writer2类:
public class Writer2 extends Thread {
private Demo2 demo;
public Writer2(Demo2 demo) {
this.demo= demo;
}
@Override
public void run() {
demo.write();
}
}
Reader2类:
public class Reader2 extends Thread {
private Demo2 demo;
public Reader2(Demo2 demo) {
this.demo= demo;
}
@Override
public void run() {
try {
demo.read();//可以收到中断的异常,从而有效退出
} catch (InterruptedException e) {
System.out.println("我不读了");
}
System.out.println("读结束");
}
}
运行结果:
开始往这个buff写入数据...
不等了,尝试中断
我不读了
读结束
可以看到调用interrupt()方法时,可以中断等待的线程。
5.公平锁
公平锁是指多个线程在等待同一个锁时,必须按照申请的时间顺序来依次获得锁;而非公平锁则不能保证这一点。非公平锁在锁被释放时,任何一个等待锁的线程都有机会获得锁。 synchronized的锁是非公平锁,ReentrantLock默认情况下也是非公平锁,但可以通过带布尔值的构造函数要求使用公平锁。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
如果指定了参数为true,默认就是非公平锁。
以上就是Synchronized和ReentrantLock的区别。