Java中的多线程与锁(二)(锁的概念)

1. 关于锁
  1. 锁的概念。
    • 锁用来起保护作用,控制对被保护对象的访问。对于一个线程来说,锁有 2 种状态,一种状态 ‘可进入’或‘可通过’,这种状态下锁对该线程没有副作用,线程继续执行,而另一种状态 ‘不可进入’或‘不可通过’,这种状态下,锁对该线程产生了约束,线程可能会立即返回或者进入等待状态,直到某个条件成立,使得该线程可以通过锁,而等待过程中,该线程也可能被外部中断唤醒。
    • 那么怎样理解:一个线程‘进入’或‘通过’锁呢?锁是边界,对于任一线程,它要么‘通过’锁,即通过这个边界,要么被这个边界阻拦,它无法越界!想想现实生活中的锁:一把锁通常会配有多把钥匙,线程拥有解锁的钥匙,便获得了通过边界的许可!
    • 锁是什么?下面是一个简单的自定义锁的例子(即上一篇文章:Java中的多线程与锁(一)中利用 synchronized 关键字和 ‘等待/通知’方法实现的自定义锁组件)
/* 简单锁实现 */
class MyLock{
	private int count = 0;
	/* 获取锁 */
	public void lock() {
		synchronized(this) {			
			while(count != 0) {
				try {
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			count++;
		}
	}
	/* 释放锁 */
	public void unLock() {
		synchronized (this) {
			if(count > 0) {
				count--;
				notify();
			}
		}
	}
}

分析‘获取锁’和‘释放锁’的逻辑(暂时不用理会 wait() 和 notify() 方法),可以看到它们都是在 synchronized 代码块中通过更改(先检查变量的值,后更新变量的值)锁的内部状态来 表达 锁的获取与释放,synchronized 保证了更改操作的排它性,同一时刻只有一个线程能成功更改锁的状态,从而根据锁的状态来决定该线程的行为:它是通过锁还是等待锁,这是一个 ‘先检查后设置’ 的操作。‘更改操作’ 的排它性是实现锁功能的核心 (该操作过程被 synchronized 保证不会受到其它线程的干扰),而 wait() 和 notify() 方法提供的‘等待 / 通知’机制则起到辅助作用,wait() 方法使得未成功获取锁的线程能够让出 CPU,而不是无限的循环尝试获取锁(无疑,这种操作方式非常低效,‘等待/通知’机制便用于高效解决该问题),wait() 方法维护了一个线程等待队列,而 notify() 方法配合 wait() 方法,使得当一个线程释放锁时,等待该锁的线程能够及时被通知/唤醒。

综上述,一个锁的基本组成有:1 排它的检查/更新操作(原子操作,更改锁的状态来表达成功获取锁或释放锁)(必须), 2 一个队列,用于维护等待锁的线程(管理等待锁的线程)(可选,线程不一定要在锁上等待,它可以过一会儿再来尝试获取锁)

锁的本质在于通过一个原子操作检查及更改一个对象实例的状态,来表达执行操作的线程是否成功获取锁以及释放锁 (而对于如何处理未成功获取锁的线程,则取决于实际需求,线程可以在锁上等待,即 锁必须维护一个线程等待队列,线程也可以立即返回结果,即 获取锁失败)。其中 ‘原子操作’是保障 ‘是否成功获取锁以及释放锁’ 的前提,而任一能满足该 ‘原子操作’ 的 ‘对象实例’ 都能作为锁来使用。这里的锁更多的是一个形式化的概念,锁是边界,它执行入境检查,决定谁可以通过(进入),这里是单向检查,只管进不管出,当然,你必须先进去,才能够从里面出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值