同步之Lock锁

    我在同步之synchronized关键字中说过synchronized真正起作用的是锁对象,不过这个“锁对象”却是隐式监视器锁,我们并不明确是在哪里加上了锁,在哪里释放了锁。为了更明确的表示这种锁的使用,我们可以使用JDK1.5提供的Lock

 

    在API中是这样介绍Lock的:Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition对象。

 

    好了,既然知道了Locksynchronized更好,那我们就使用Lock修改同步之synchronized关键字里面的例子,不过Lock是一个接口,但是don't worry,它也提供了实现类ReentrantLock给我们使用。


package com.gk.thread.mutex.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable {

	private int tickets = 18;
	private final Lock lock = new ReentrantLock();
	
	@Override
	public void run() {
		
		while(true) {
			
			// 在对共享数据tickets操作之前加锁
			lock.lock();
			
			try {
				
				if(tickets > 0) {
					System.out.println(Thread.currentThread().getName() + "正在出票...   " + tickets--);
					
					try {
						Thread.sleep(1 * 10);
					} catch (InterruptedException e) {
						throw new RuntimeException(e);
					}
				}else {
					break;
				}
				
			}finally {
				
				// 在finally确保锁得到了释放。
				lock.unlock();
			}
			
		}
		
	}

}


    测试代码

package com.gk.thread.mutex.lock;

public class Test {
	
	public static void main(String[] args) {
		
		Runnable r = new SellTicket();
		
		new Thread(r, "窗口1").start();
		new Thread(r, "窗口2").start();
		new Thread(r, "窗口3").start();
		
	}

}


    上面ReentrantLock的使用是典型的用法,使用 lock 块来调用try,并在finally中确保锁能被释放。

 

    尽管相对于使用synchronized来说,使用显式Lock所需的代码要多一些,但这也是显式Lock对象的优点之一。如果在使用synchronized关键字时,某些事物失败了,那么就会抛出一个异常。但是你没有机会去做清理工作为维护系统使其处于良好的状态。有了显式的Lock对象,你就可以使用finally子句将系统维护在正确的状态了。

 

    显式Lock还提供了tryLock()方法,它可以尝试着获取锁并且最终获取的锁会失败,或者尝试着获取锁一段时间然后放弃它。


    下面例子出自《java编程思想 第4版》,不过对其进行了小小的改动。


package com.gk.thread.mutex.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TryLocking {
	
	private Lock lock = new ReentrantLock();
	
	public void timed() {
		
		boolean flag = false;
		
		try {
			flag = lock.tryLock(2, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		
		try {
			
			System.out.println("lock.tryLock(2, TimeUnit.SECONDS) : " + flag);
		}finally{
			if(flag) {
				lock.unlock();
			}
		}
	}
	
	public void untimed() {
		
		boolean flag = lock.tryLock();
		
		try {
			
			System.out.println("lock.tryLock() : " + flag);
		}finally {
			if (flag) {
				lock.unlock();
			}
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		
		final TryLocking t = new TryLocking();
		
		t.untimed();
		t.timed();
		
		// 匿名类
		new Thread() {
			{
				 setDaemon(true);	// 设置为守护线程
			}
			public void run() {
				t.lock.lock();
				System.out.println("得到了锁");
				/*
				 * 由于将本线程设置为守护线程,
				 * 所以我在这里加了个死循环,从中也能看出守护线程的特点
				 */
				while(true) {}	
			};
		}.start();
		
		//Thread.yield();
		Thread.sleep(1 * 1000);		// 将yield换成sleep更能看出效果
		
		t.untimed();
		t.timed();
	}

}



        ReentrantLock允许你尝试着获取锁但最终未获得锁,这样如果其它线程已经获得了这个锁,那么你就可以决定离开去执行其他一些事情,而不是等待直到这个锁被释放,就像在untimed()方法中所看到的。在timed()方法中,做出了尝试去获取锁,该尝试可以在2秒后失败。在main()中,作为匿名类而创建了一个单独的Thread,它将获取锁,这使得untimed()timed()方法对某些事物将产生了竞争。

 

        显式Lock对象在加锁和释放锁方面,相对于内建的synchronized锁来说,还赋予了更细粒度的控制力。这对于实现专有同步结构是很有用的,例如用于遍历链接列表中的节点的节点传递的加锁机制(也称为锁耦合),这种遍历代码必须在释放当前节点的锁之前捕获下一个节点的锁。   



参考资料

    《java编程思想 第4版》





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值