锁(八)

在java多线程中,我们知道使用synchronized关键字来实现线程间的同步互斥操作,那么其实还有一个更优秀的机制去完成“同步互斥”工作,它就是Lock对象,即重入锁和读写锁。他们具有比synchronized更为强大的功能,并且具有嗅探锁定、多路分支等功能。

ReentrantLock(重入锁)

  • 概念:
    在需要进行同步的代码部分加上锁定,但最后一定要释放锁,不然会造成锁用于无法释放,其他线程永远进不来的结果。
public class UseReentrantLock {

	final Lock lock = new ReentrantLock();
	
	public void method1(){
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入method1...");
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName() + "退出method1...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally{
			lock.unlock();
		}
		
	}
	
	public void method2(){
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入method2...");
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName() + "退出method2...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally{
			lock.unlock();
		}
	}
	
	public static void main(String[] args) {
		final UseReentrantLock url = new UseReentrantLock();
		
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				url.method1();
				url.method2();
			}
		},"t1");
		
		t1.start();
	}

}

/*
打印结果:
	t1进入method1...
	t1退出method1...
	t1进入method2...
	t1退出method2...
*/

锁与等待/通知

我们在使用synchronized的时候,如果需要多线程间进行协作工作则需要Object的wait()和notify()、notifyAll方法配合使用。同样,我们在使用Lock的时候,可以使用一个新的等待/通知的类Condition,这个Condition一定是针对具体的某一把锁,也就是在只有锁的基础之上才会产生Condition。

public class UseCondition {

	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	
	public void method1(){
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入等待状态...method1...");
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName() + "释放锁...");
			condition.await(); //await 释放锁
			System.out.println(Thread.currentThread().getName() + "继续执行...method1...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally{
			lock.unlock();
		}
		
	}
	
	public void method2(){
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入method2...");
			Thread.sleep(3000);
			System.out.println(Thread.currentThread().getName() + "发出唤醒...");
			condition.signal(); //notify 不释放锁
			System.out.println(Thread.currentThread().getName() + "不释放锁...");
			System.out.println(Thread.currentThread().getName() + "执行完毕...method2...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally{
			lock.unlock();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		final UseCondition uc = new UseCondition();
		
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				uc.method1();
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable(){
			@Override
			public void run() {
				uc.method2();
			}
		},"t2");
		
		t1.start();
		Thread.sleep(1000);
		t2.start();

	}
}
/*
打印结果:
	t1释放锁...
	t2进入method2...
	t2发出唤醒...
	t2不释放锁...
	t2执行完毕...method2...
	t1继续执行...method1...
*/

多个Condition

可以通过一个Lock对象产生多个Condition进行线程间的交互,非常的灵活。可以使得部分需要唤醒的线程唤醒,其他的线程则继续等待通知。

public class UseManyCondition {

	private Lock lock = new ReentrantLock();
	
	private Condition c1 = lock.newCondition();
	
	private Condition c2 = lock.newCondition();
	
	public void method1(){
		
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入method1...");
			Thread.sleep(1000);
			c1.await();
			System.out.println(Thread.currentThread().getName() + "退出method1...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally{
			lock.unlock();
		}
	}
	
	public void method2(){
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入method2...");
			Thread.sleep(1000);
			c1.await();
			System.out.println(Thread.currentThread().getName() + "退出method2...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally{
			lock.unlock();
		}
	}
	
	public void method3(){
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入method3...");
			Thread.sleep(1000);
			c2.await();
			System.out.println(Thread.currentThread().getName() + "退出method3...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally{
			lock.unlock();
		}
	}
	
	public void method4(){
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入method4...");
			c1.signalAll();
			System.out.println(Thread.currentThread().getName() + "c1释放锁...");
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			lock.unlock();
		}
	}
	
	public void method5(){
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "进入method5...");
			c2.signal();
			System.out.println(Thread.currentThread().getName() + "c2释放锁...");
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			lock.unlock();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		final UseManyCondition umc = new UseManyCondition();
		
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				umc.method1();
			}},"t1");
		
		Thread t2 = new Thread(new Runnable(){
			@Override
			public void run() {
				umc.method2();
			}},"t2");
		
		Thread t3 = new Thread(new Runnable(){
			@Override
			public void run() {
				umc.method3();
			}},"t3");
		
		Thread t4 = new Thread(new Runnable(){
			@Override
			public void run() {
				umc.method4();
			}},"t4");
		
		Thread t5 = new Thread(new Runnable(){
			@Override
			public void run() {
				umc.method5();
			}},"t5");
		
		t1.start();
		t2.start();
		t3.start();
		
		Thread.sleep(1000);
		
		t4.start();
		t5.start();
	}

}
/*
打印结果:
t1进入method1...
t3进入method3...
t2进入method2...
t4进入method4...
t4c1释放锁...
t5进入method5...
t5c2释放锁...
t1退出method1...
t2退出method2...
t3退出method3...
*/

Lock/Condition其他用法

公平锁和非公平锁:ReentrantLock lock = new ReentrantLock(boolean isFair),lock用法:

  • lock.tryLock():
    尝试获得锁,获得结果用true/false返回。
  • lock.tryLock(paramLong, paramTimeUnit):
    在给定的时间内尝试获得锁,获得结果用true/false返回。
  • lock.isFair():
    是否公平锁。
  • lock.isLocked():
    是否锁定。
  • lock.getHoldCount():
    查询当前线程保持此锁的个数,也就是调用lock()次数。
  • lock.lockInterruptibly():
    优先响应中断的锁。
  • lock.getQueueLength():
    返回正在等待获取此锁的线程数。
  • lock.getWaitQueueLength(Condition paramCondition):
    返回等待与锁定相关的给定条件Condition的线程数。
  • lock.hasQueuedThread(Thread paramThread):
    查询指定的线程是否正在等待此锁。
  • lock.hasQueuedThreads():
    查询是否有线程正在等待此锁。
  • lock.hasWaiters(Condition paramCondition):
    查询是否有线程正在等待与此锁定有关的condition条件。

ReentrantReadWriteLock(读写锁)

读写锁,其核心就是实现读写分离。在高并发访问下,尤其是读多写少的情况下,性能要员高于重入锁。对于synchronized、ReentrantLock,我们知道,同一时间内,只能有一个线程进行访问被锁定的代码。读写锁则不同,其本质是分成两个锁,即读锁和写锁。在读锁下,多个线程可以并发的进行访问,但是在写锁的时候,只能一个一个顺序访问,即读读共享,写写互斥,读写互斥。

public class UseReentrantReadWriteLock {

	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	private ReadLock readLock = lock.readLock();
	private WriteLock writeLock = lock.writeLock();
	
	public void read(){
		try {
			readLock.lock();
			System.out.println(Thread.currentThread().getName() + "进入read...");
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName() + "退出read...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally{
			readLock.unlock();
		}
	}
	
	public void write(){
		try {
			writeLock.lock();
			System.out.println(Thread.currentThread().getName() + "进入write...");
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName() + "退出write...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally{
			writeLock.unlock();
		}
	}
	
	public static void main(String[] args) {
		final UseReentrantReadWriteLock urrwl = new UseReentrantReadWriteLock();
		
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				urrwl.read();
			}},"t1");
		
		Thread t2 = new Thread(new Runnable(){
			@Override
			public void run() {
				urrwl.read();
			}},"t2");
		
		Thread t3 = new Thread(new Runnable(){
			@Override
			public void run() {
				urrwl.write();
			}},"t3");
		
		Thread t4 = new Thread(new Runnable(){
			@Override
			public void run() {
				urrwl.write();
			}},"t4");
		
		//读读共享
		t1.start();
		t2.start();
		
		//读写互斥
		t1.start();
		t3.start();
		
		//写写互斥
		t3.start();
		t4.start();
	}

}
/*
打印结果:
t1进入read...
t2进入read...
t2退出read...
t1退出read...
t3进入write...
t3退出write...
 * */

锁优化

  1. 避免死锁。
  2. 减小锁持有时间。
  3. 减小锁的粒度。
  4. 锁的分离。
  5. 尽量使用无锁的操作,如原子操作(Atomic系列类),volatile关键字。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值