ReentrantLock与Synchronized的区别

文章讨论了Java中synchronized和ReentrantLock两种锁机制,强调它们在保证线程安全和原子性操作方面的作用。synchronized是内置锁,可重入但无法设置公平性,而ReentrantLock提供了更多的控制,如尝试获取锁、响应中断和可配置的公平性。ReentrantLock的使用更加灵活,但也需要程序员手动管理锁的释放。
摘要由CSDN通过智能技术生成

一、Synchronized

        synchronized同步锁是一种可重入锁(能被同一个线程反复获取的锁),代码块在任意时刻只能执行一个线程,保证了一段代码的原子性。

       有如下一段代码:

public class Test {

	public static void main(String[] args) throws InterruptedException {
		
		Thread add = new AddThread();
		Thread dec = new DecThread();
		
		add.start();
		dec.start();
		
		add.join();
		dec.join();
		
		System.out.println(Counter.count);
		

	}

}

class Counter{
	
	public static int count = 0;
}

class AddThread extends Thread{
	public void run() {
		for(int i = 0;i < 2000;i++) {
				Counter.count += 1;
		}
	}
}

class DecThread extends Thread{
	public void run() {
		for(int i = 0;i < 800;i++) {
				Counter.count -= 1;
		}
	}
}

       我们理想的结果应该是1200,但实际上这段代码的运行结果是不确定的,这是因为多个线程同时读写共享变量,就会出现数据不一致的问题。所以引入了一个锁的概念来确保此操作的原子性。

  synchronized的用法:

        1、修饰一个代码块:一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。

// 计数器类
public class Counter{
	
	// 用于计数的公共变量
	public static int count = 0;
	
	// 递增
	public void add() {
		
		for (int i = 0; i < 10000; i++) {
			synchronized (this) {
				Counter.count += 1;
			}
		}
	}
	
	// 递减
	public void dec() {
		for (int i = 0; i < 10000; i++) {
			synchronized (this) {
				Counter.count -= 1;
			}
		}
	}
}

测试类

public class Test {

	public static void main(String[] args) throws InterruptedException {
		
		Counter counter = new Counter();
		
		Thread add = new Thread() {
			@Override
			public void run() {
				counter.add();
			}
		};
		
		Thread dec = new Thread() {
			@Override
			public void run() {
				counter.dec();
		};
		
		add.start();
		dec.start();
		
		add.join();
		dec.join();
		
		System.out.println(Counter.count);//0

	}

}

        2、修饰一个实例方法:其作用范围是整个方法,作用的对象是调用这个方法的对象。

public class Counter{
	
		// 用于计数的公共变量
		public static int count = 0;
		
		// 递增
		public synchronized void add() {
			for (int i = 0; i < 10000; i++) {
					Counter.count += 1;
			}
		}
		
		// 递减
		public synchronized void dec() {
			for (int i = 0; i < 10000; i++) {
					Counter.count -= 1;
			}
		}
}

        3、修饰一个静态方法:其作用的范围是整个静态方法,作用的对象是这个类的所有对象。

public class Counter{
	
		// 用于计数的公共变量
		public static int count = 0;
		
		// 递增
		public synchronized static  void add() {
			for (int i = 0; i < 10000; i++) {
					Counter.count += 1;
			}
		}
		
		// 递减
		public synchronized static void dec() {
			for (int i = 0; i < 10000; i++) {
					Counter.count -= 1;
			}
		}
}

二、ReentrantLock

        synchronized关键字获取时必须一直等待,没有额外的尝试机制。所以,引入了一个ReentrantLock可重入锁。它是由java.util.concurrent.locks包提供的,和synchronized一样,一个线程可以多次获取同一个锁。

import java.util.concurrent.locks.ReentrantLock;

public class Counter {
	
	public static int count = 0;
	
	//创建锁
	private final ReentrantLock lock = new ReentrantLock();
	
	public void add() {
		for(int i = 0;i < 1230;i++) {
			//加锁
			lock.lock();
			try {
				Counter.count += 1;
			}finally {
				//释放锁
				lock.unlock();
			}
			
		}
	}
	
	public void dec() {
		for(int i = 0;i < 1000;i++) {
			lock.lock();
			
			try {
				Counter.count -= 1;
			}finally {
				lock.unlock();
			}
		}
	}

}

值得注意的是我们创建ReentrantLock锁,结束后要在finally中正确释放锁。

ReentrantLock和synchronized有一个不同的地方,就是ReentrantLock可以尝试获取锁

public void add() throws InterruptedException {
		for(int i = 0;i < 1230;i++) {
			//尝试获取锁
            //最多等待1秒。如果1秒后仍未获取到锁,tryLock()返回false
			if(lock.tryLock(1, TimeUnit.SECONDS)) {
			try {
				Counter.count += 1;
			}finally {
				//释放锁
				lock.unlock();
			}
		}
			
	}
}

        所以,使用ReentrantLock比直接使用synchronized更安全,线程在tryLock()失败的时候不会导致死锁。 

 三、ReentrantLock与synchronized的区别 

ReentrantLockSynchronized
用法用于代码块

可以修饰普通方法、静态方法、

代码块

锁实现机制AQS监视器Monitor
获取锁可以通过tryLock()尝试获取锁线程抢占
释放锁必须显示通过unlock()释放锁自动释放
锁类型支持公平锁和非公平锁非公平锁
可重入性可重入可重入
响应中断可以响应中断不可以响应中断

总结:

        1、用法不同:synchronized 可以用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用于代码块。

        2、获取锁和释放锁不同:synchronized 是自动加锁和释放锁,而 ReentrantLock 需要手动加锁和释放锁。

        3、锁类型不同:synchronized 是非公平锁,而 ReentrantLock 默认为非公平锁,但它也可以手动被指定为公平锁。

        4、响应中断不同:ReentrantLock 可以响应中断,解决死锁的问题,而 synchronized 不能响应中断。

        5、底层实现不同:synchronized 是 JVM 层面通过监视器实现的,而 ReentrantLock 是基于 AQS 实现的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值