【java并发】synchronized和ReentrantLock

1 简介

synchronized和ReentrantLock都是可重入锁,即单个线程中可以锁多次。

2 区别

ReentrantLock包含特性:

  • 等待可中断

当持有锁的线程长期不释放锁,正在等待的线程可以选择放弃等待。

  • 公平锁

多个线程在等待同一个锁,必须按照申请锁的顺序来依次获得锁;而非公平锁则没有限制。synchronized和ReentrantLock默认都是非公平锁,ReentrantLock可通过修改属性构建公平锁。

  • 多条件控制

一个ReentrantLock对象可以绑定多个Condition对象,而一个synchronized关联的锁对象只能对应一个条件。

来一个典型的消费者生产者例子:

当生产仓库满时,生产者需要等待消费者消费之后才继续生产

当生产仓库空时,消费者需要等待生产者生产之后才继续消费

synchronized方式:

public class SyncProductTest {

    public static volatile int productNum=7;
    public static volatile int productMaxNum=10;
    
    public static Object productor = new Object();
    
    public static void main(String[] args) {
	for(int i=0;i<20;i++){
	    new Thread(new ConsumeThread()).start();
	}
	for(int i=0;i<20;i++){
	    new Thread(new ProductThread()).start();
	}
    }
    
}
class ProductThread implements Runnable{

    @Override
    public void run() {
	synchronized (SyncProductTest.productor) {
	    while(SyncProductTest.productNum==SyncProductTest.productMaxNum){
		System.out.println(Thread.currentThread().getName()+"product is full");
		try {
		    System.out.println(Thread.currentThread().getName()+"productor wait...");
		    SyncProductTest.productor.wait();
		    System.out.println(Thread.currentThread().getName()+"productor wait over...");
		} catch (InterruptedException e) {
		    e.printStackTrace();
		}
	    }
	    SyncProductTest.productNum++;
	    SyncProductTest.productor.notifyAll();
	}
    }
    
}
class ConsumeThread implements Runnable{

    @Override
    public void run() {
	synchronized (SyncProductTest.productor) {
	    while(SyncProductTest.productNum==0){
		System.out.println(Thread.currentThread().getName()+"product is empty");
		try {
		    System.out.println(Thread.currentThread().getName()+"consumer wait...");
		    SyncProductTest.productor.wait();
		    System.out.println(Thread.currentThread().getName()+"consumer wait over...");
		} catch (InterruptedException e) {
		    e.printStackTrace();
		}
	    }
	    SyncProductTest.productNum--;
	    SyncProductTest.productor.notifyAll();
	}
    }
    
}

ReentrantLock方式:

public class LockProductTest {

    public static volatile int productNum=7;
    public static volatile int productMaxNum=10;
    
    public static ReentrantLock productorLock = new ReentrantLock();
    public static Condition productCondition = productorLock.newCondition();
    public static Condition consumeCondition = productorLock.newCondition();
    
    public static void main(String[] args) {
	for(int i=0;i<20;i++){
	    new Thread(new ConsumeThread()).start();
	}
	for(int i=0;i<20;i++){
	    new Thread(new ProductThread()).start();
	}
    }
    
}
class ProductLockThread implements Runnable{

    @Override
    public void run() {
	try{
	    LockProductTest.productorLock.lock();
	    while(LockProductTest.productNum==LockProductTest.productMaxNum){
		System.out.println(Thread.currentThread().getName()+"product is full");
		try {
		    System.out.println(Thread.currentThread().getName()+"productor wait...");
		    LockProductTest.productCondition.await();
		    System.out.println(Thread.currentThread().getName()+"productor wait over...");
		} catch (InterruptedException e) {
		    e.printStackTrace();
		}
	    }
	    LockProductTest.productNum++;
	    LockProductTest.consumeCondition.signalAll();
	}finally{
	    LockProductTest.productorLock.unlock();
	}
	}
    }
    
class ConsumeLockThread implements Runnable{

    @Override
    public void run() {
	try{
	  LockProductTest.productorLock.lock();
	    while(LockProductTest.productNum==0){
		System.out.println(Thread.currentThread().getName()+"product is empty");
		try {
		    System.out.println(Thread.currentThread().getName()+"consumer wait...");
		    LockProductTest.consumeCondition.await();
		    System.out.println(Thread.currentThread().getName()+"consumer wait over...");
		} catch (InterruptedException e) {
		    e.printStackTrace();
		}
	    }
	    LockProductTest.productNum--;
	    LockProductTest.productCondition.signalAll();
	}finally{
	    LockProductTest.productorLock.unlock();
	}
    }
    
}

第一种方式,生产者消费者通过同一个对象锁进行等待和唤醒,会照成无效唤醒,比如一个生产者线程唤醒了另一个生产者线程,一个消费者唤醒了另一个,而我们实际需要的是生产者线程生产后去唤醒消费者去消费,所以第二种分条件唤醒的方式更合适。

3 总结

  • 如果需要细粒度的控制锁过程,可以选择使用ReentrantLock
  • 如果不需要,建议1.5之后都使用synchronized,JVM一直在优化,应该是比自己处理Lock过程更可靠

 


爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qqchaozai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值