开始
今天,我们来聊聊java中的锁。
锁的三个问题
锁是一种互斥的机制,在多线程环境中实现对资源的强制控制,以帮助实现并发控制。
锁的使用会带来三个问题:
1. 锁开销。锁会占用内存空间。锁的初始化,销毁,获取和释放,都需要消耗CPU时间。
2. 锁竞争。一个线程试图在另一个线程已经获取锁的资源上获取锁时,就会发生锁竞争。
3.死锁。至少有两个线程,都在等待获取对方所持有的锁时,就会发生死锁。死锁有一个著名的案例,即哲学家就餐问题。说有五个哲学家,围坐就餐,每两个人的中间放一只筷子,即有五支筷子。得到开饭指令后,每人都先去拿右边的筷子,再去拿左边的筷子,拿到一双筷子就可以开始吃饭。分析这个案例,我们发现,如果五个人都处于一个竞争的状态的话,那么,每个人都会处在一个等待左边的人吃完的状态。于是,就发生了死锁。
基于以上三个问题,就要求在使用锁的时候,需要一定的策略性。如优化锁的获取和释放机制,更细粒度的去加锁等。
Synchronized
synchronized关键字可以帮助我们给一个类对象或者实例对象加锁。常见用法如加在某个方法上,加在代码块上。当加在方法上时,如果该方法是静态方法,则锁住的是类对象,如加在非静态方法上,则锁住的是实例对象。当加在代码块上时,要看给的参数是什么,如果是***.class,则锁住的是类对象,如果是this或某个实例,则锁住的是当前实例对象或指定的实例对象。
这里,有个问题是锁住的目标和被锁定的代码块之间,存在什么样的关系?必须深刻理解这个问题,才算是真正明白了synchronized关键字。
想象有若干个房间,每个房间都上了锁,当要进入某个房间时,就必须先拿到这个房间的钥匙,必然,前提就是钥匙没有被别人拿走。线程也是如此,被锁定的方法或代码块就好比房间,锁住的目标就相当于钥匙。接下来,我们引入同步和异步的概念,帮助我们做接下来的分析。
线程同步指的是,若干线程不可以同时执行,当某一个线程处于运行状态时,其他线程一定处于就绪或阻塞状态。
线程异步指的是,若干线程是可以同时执行的,他们之间不存在相互影响,当一个线程处于运行状态时,其他线程也可以运行。
联系到锁的话,我们得到,若干线程同步的条件之一是,他们要执行的方法或代码块,使用了同样的锁定对象。假设有类A,有a、b两个线程使用了synchronized关键字,c是A类的一个静态类变量。
事实上,实际情况比上表要复杂,但是判断的核心依据是相同的:是不是同一把锁(同一把钥匙)。
Lock
Lock是另一种帮助加锁的机制。使用Lock时,你需要一个Lock对象,例如lock。你需要通过调用
lock的lock、unlock等方法,为某段代码加锁。使用lock时,判断多个线程是同步或异步,核心判断依据是所使用的lock是不是同一个对象,是即为同步,不是即为异步。
例如,A类包含一个Lock类型的类变量lock,可能通过构造方法或者lock的set方法等,传入一个Lock实例到lock变量。A类中,代码块a、b都使用了lock的相关方法加锁。现在有线程t1和t2,那么,他们在调用a或b时,是同步还是异步呢?
我们说,主要取决于在给类变量lock赋值时,给的是不是同一个实例。如果都给lock1,就是同步,一个给lock1,另一个给lock2,就是异步。
可重入锁
一个线程,可以对自己已经获取锁的对象再次加锁(线程内部,如递归),称之为可重入锁。可重入锁的实现机制一般是给锁加一个计数标识。线程每获取锁一次,该计数标识加1,每释放锁一次,该计数标识减1。当减至0时,说明该线程完全释放了目标对象的锁。
【关键词:青岛Java培训,Java就业培训,Java培训费用,Java培训哪家好,中享思途】