并发编程原子操作 AQS 思想之Lock锁的标准使用(十二)

今天我们分析并发编程原子操作 AQS 思想之Lock 的标准使用
一、显式锁和 AQS:
1、显式锁
有了 synchronized 为什么还要 Lock ? Java 程序是靠 synchronized 关键字实现锁功能的,使用 synchronized 关键字;将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。
 
Lock 的标准用法:

代码示例:

public class LockCase {
	private Lock lock = new ReentrantLock();
	private int age = 100000;//初始100000
    private static class TestThread extends Thread{
    	
    	private LockCase lockCase;
    	
    	public TestThread(LockCase lockCase, String name) {
    		super(name);
    		this.lockCase = lockCase;
		}
    	
    	@Override
	    public void run() {
    		for(int i=0;i<100000;i++) {//递增100000
    			lockCase.test();
    		}
			System.out.println(Thread.currentThread().getName()
					+" 递增后age =  "+lockCase.getAge());
	    }
    }
    
	public void test() {
		lock.lock();
		try{
			age++;
		}finally {
			lock.unlock();
		}
	}
	
	public void test2() {
		lock.lock();
        try {
        	age--;
        } finally {
            lock.unlock();
        }
	}
	
	public int getAge() {
		return age;
	}

	public static void main(String[] args) throws InterruptedException {
		LockCase lockCase = new LockCase();
		Thread endThread = new TestThread(lockCase,"endThread");
		endThread.start();
		Thread.sleep(1000);
		for(int i=0;i<100000;i++) {//递减100000
			lockCase.test2();
		}
		System.out.println(Thread.currentThread().getName()
				+" 递减后mainAge =  "+lockCase.getAge());
	}
}

 执行结果:

finally 块中释放锁,目的是保证在获取到锁之后,最终能够被释放。 不要将获取锁的过程写在 try 块中,因为如果在获取锁(自定义锁的实现)。时发生了异常,异常抛出的同时,也会导致锁无故释放。
 
Lock 的常用 API:

代码展示: 

2、ReentrantLock
锁的可重入
简单地讲 就是:“同一个线程对于已经获得到的锁,可以多次继续申请到该 锁的使用权”。而 synchronized 关键字隐式的支持重进入,比如一个 synchronized 修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得 该锁。ReentrantLock 在调用 lock() 方法时,已经获取到锁的线程,能够再次调用 lock()方法获取锁而不被阻塞。
 
公平和非公平锁
如果在时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的, 反之,是不公平的。公平的获取锁,也就是等待时间最长的线程最优先获取锁, 也可以说锁获取是顺序的。 ReentrantLock 提供了一个构造函数,能够控制锁是 否是公平的。事实上,公平的锁机制往往没有非公平的效率高。 在激烈竞争的情况下, 非公平锁的性能高于公平锁的性能的一个原因是 : 在恢 复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟。假设线程 A 持有一个锁, 并且线程 B 请求这个锁。由于这个锁已被线程 A 持有 , 因此 B 将被挂 起。当 A 释放锁时 ,B 将被唤醒 , 因此会再次尝试获取锁。与此同时 , 如果 C 也请求 这个锁, 那么 C 很可能会在 B 被完全唤醒之前获得、使用以及释放这个锁。这样 的情况是一种“双赢”的局面:B 获得锁的时刻并没有推迟 ,C 更早地获得了锁 , 并 且吞吐量也获得了提高。代码示例如上:
 
3、读写锁 ReentrantReadWriteLock
之前提到锁(如 Mutex ReentrantLock )基本都是排他锁,这些锁在同一 时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问, 但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对 锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁 有了很大提升。 除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁能够简化 读写交互场景的编程方式。假设在程序中定义一个共享的用作缓存数据结构,它 大部分时间提供读服务(例如查询和搜索),而写操作占有的时间很少,但是写 操作完成之后的更新需要对后续的读服务可见。 在没有读写锁支持的(Java 5 之前)时候,如果需要完成上述工作就要使用 Java 的等待通知机制,就是当写操作开始时,所有晚于写操作的读操作均会进入 等待状态,只有写操作完成并进行通知之后,所有等待的读操作才能继续执行(写
操作之间依靠 synchronized 关键进行同步),这样做的目的是使读操作能读取到 正确的数据,不会出现脏读。改用读写锁实现上述功能,只需要在读操作时获取 读锁,操作时获取写锁即可。当写锁被获取到时,后续(非当前写操作线程) 的读写操作都会被阻塞,写锁释放之后,所有操作继续执行,编程方式相对于使 用等待通知机制的实现方式而言,变得简单明了。一般情况下,读写锁的性能都会比排它锁好,因为大多数场景读是多于写的。在读多于写的情况下,读写锁能够提供比排它锁更好的并发性和吞吐量 ReentrantReadWriteLock 其实实现的是 ReadWriteLock 接口
 
示例代码:
public class UseRwLock {

    private final ReadWriteLock lock = new ReentrantReadWriteLock();//定义一个读写锁
    private final Lock getLock = lock.readLock();//读锁
    private final Lock setLock = lock.writeLock();//写锁

  //读操作
    public GoodsInfo getNum() {
        getLock.lock();
        try{
            SleepTools.ms(5);
            return null;
        }finally {
            getLock.unlock();
        }
    }

   //写操作
    public void setNum(int number) {
        setLock.lock();
        try{
            SleepTools.ms(5);
             //XXX
        }finally {
            setLock.unlock();
        }
    }
}

 执行性能:

如果用内置锁:
 
	public synchronized GoodsInfo getNum() {
		 //XXXX
	}

	 
	public synchronized void setNum(int number) {
		  //XXXX
	}

执行效率:

基本分析到此完成。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

寅灯

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

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

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

打赏作者

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

抵扣说明:

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

余额充值