ReentrantLock与synchronized的区别

一、定义

synchronized:是个关键字,是一种可重入的锁。在进行加锁时,可以直接将synchronized写入方法名中,或者使用synchronized代码块。synchronized在进行加锁时,会记录获取锁的次数和释放锁的次数。

ReentrantLock:是个类,是一种可重入的锁。所以用该类进行加锁时,需要先创建其对象,使用对象调用方法进行加锁。ReentrantLock在获取锁时,可以调用tryLock()方法尝试获取锁,给其设定一个时间,超过这个时间没有获取到锁,就不再等待,可以去做别的处理。

二、代码实现案例

synchronized三种加锁方式

1、定义一个公共的对象充当锁

public class Demo02 {
    public static void main(String[] args) throws InterruptedException {
    	//创建2个线程对象
		Thread t1 = new AddThread();
		Thread t2 = new DecThread();
		
		//启动线程
		t1.start();
		t2.start();
		
		//插队到Main线程之前
		t1.join();
		t2.join();
		
		//输出count变量的值
		System.out.println(Counter.count);
	}
}

//计数器类
class Counter{
	//公共的静态成量变量
	public static int count = 0;
	
	//公共的锁对象
	public static final Object lock = new Object();
}

//递增线程
class AddThread extends Thread{
	@Override
	public void run() {
		for(int i=0;i<10000;i++) {
			//两个线程使用同一把锁(使用同一个对象)
			synchronized (Counter.lock) {
				//对count变量加1
				Counter.count += 1;
			
			}
		}
	}
}

//递减线程
class DecThread extends Thread{
	@Override
	public void run() {
		for(int i=0;i<10000;i++) {
			//两个线程使用同一把锁(使用同一个对象)
			synchronized (Counter.lock) {
				//对count变量减1
				Counter.count -= 1;
			}
		}
	}
}

2、使用当前this对象(必须是同一对象调用多个线程)

这种情况下既可以使用synchronized代码块,传入this对象进行加锁,也可以直接将synchronized写在方法名上,也表示的是使用this对象。但是这两种方法它们作用的范围不一样,第一种加锁只作用在Counter1.count += 1;这一句上,第二种加锁是对整个方法起作用。

public class Counter1 {
	// 用于计数的公共变量
		public static int count = 0;
		
		// 递增
		public void add() {
			for (int i = 0; i < 10000; i++) {
				//当前this对象
				synchronized (this) {
					Counter1.count += 1;
				}
			}
		}
		
		// 递减
		public synchronized void dec() {
			for (int i = 0; i < 10000; i++) {
				//当前this对象
				synchronized (this) {
					Counter1.count -= 1;
				}
			}
		}
}
public class Test1 {
	public static void main(String[] args) throws InterruptedException {
		//创建计数器类对象
		Counter1 counter1 = new Counter1();
		
		//使用当前(this)对象进行加锁
		Thread t1 = new Thread() {
			@Override
			public void run() {
				//必须是同一对象调用线程任务
				counter1.add();
			}
		};
		//使用当前(this)对象进行加锁
		Thread t2 = new Thread() {
			@Override
			public void run() {
				//必须是同一对象调用线程任务
				counter1.dec();
			}
		};
		//启动线程
		t1.start();
		t2.start();
		
		//插入到Main线程前执行
		t1.join();
		t2.join();
		
		//输出count变量的值
		System.out.println(Counter1.count);
	}
}

3、使用当前对象的class对象

public class Counter2 {
	// 用于计数的公共变量
		public static int count = 0;
		
		// 递增
		public void add() {
			for (int i = 0; i < 10000; i++) {
				//当前class对象
				synchronized (Counter2.class) {
					Counter2.count += 1;
				}
			}
		}
		
		// 递减
		public void dec() {
			for (int i = 0; i < 10000; i++) {
				//当前class对象
				synchronized (Counter2.class) {
					Counter2.count -= 1;
				}
			}
		}
}
public class Test2 {
	public static void main(String[] args) throws InterruptedException {
		
		//创建两个对象
		Counter2 counterOps01 = new Counter2();
		Counter2 counterOps02 = new Counter2();
		
		//线程1
		//两个对象调用不同的线程,使用当前类的class对象进行加锁
		Thread t1 = new Thread() {
			@Override
			public void run() {
				//不同的对象调用不同的线程任务
				counterOps01.add();
			}
		};
		
		//线程2
		Thread t2 = new Thread() {
			@Override
			public void run() {
				//不同的对象调用不同的线程任务
				counterOps02.dec();
			}
		};
		
		//启动线程
		t1.start();
		t2.start();
		
		//插入到Main线程前执行
		t1.join();
		t2.join();
		
		//输出count变量的值
		System.out.println(Counter2.count);
	}
}
ReentrantLock类加锁方式
public class Demo01 {
	public static void main(String[] args) throws InterruptedException {
		
		//创建对象
		Counter counter = new Counter();
		
		//线程1
		Thread t1 = new Thread() {
			@Override
			public void run() {
				counter.add();
			}
		};
		
		//线程2
		Thread t2 = new Thread() {
			@Override
			public void run() {
				counter.dec();
			}
		};
		
		//启动线程
		t1.start();
		t2.start();
		
		//插入到Main线程前执行
		t1.join();
		t2.join();
		
		//输出count变量的值
		System.out.println(Counter.count);
	}
}

//计算器类
class Counter {
	//公共的静态成量变量
	public static int count = 0;
	
	//创建公共的锁对象
	private final ReentrantLock lock = new ReentrantLock();
	
	//递增
	public void add() {
		for(int i=0;i<10000;i++){
			//加锁(调用lock()方法)
			lock.lock();
			try {
				Counter.count += 1;
				
				//为啥不将释放锁写入这里?
				//因为如果这里的代码一旦抛出异常就会导致锁释放不了
				//而finally代码块中的代码无论何种情况都会执行
				//lock.unlock();
			}finally {
				//释放锁(调用lock()方法)
				lock.unlock();
			}
		}
	}
	
	//递减
	public void dec() {
		for(int i=0;i<10000;i++){
			//加锁
			lock.lock();
			try {
				Counter.count -= 1;
			}finally {
				//释放锁
				lock.unlock();
			}
		}
	}
}
ReentrantLock获取锁的两种策略:一是非公平策略:线程每次尝试获取锁时,不会让等待久的线程获得锁。二是公平策略:线程每次尝试获取锁时,都会让等待最久的线程优先获得锁。
可以通过创建对象时,传入的参数确定采用哪种策略,传入true表示为公平策略,否则为非公平策略(默认采用非公平策略)。

三、相同点和不同点

1、相同点:都是一种可重入的锁。

2、不同点:synchronized获取锁的方式是线程之间进行抢占,而ReentrantLock获取锁的方式是尝试获取锁。synchronized释放锁是自动释放,而ReentrantLock释放锁是调用unlock()。synchronized获取锁的策略是非公平策略,而ReentrantLock获取锁的策略是非公平策略和公平策略。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值