Synchronized和ReentrantLock的区别

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的区别。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值