Lock接口
void lock()
如果锁被占用则会一直等待,直到其他线程释放锁,当前线程获取到锁为止
示例代码:
package com.hzw.subject1.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
static Lock lock = new ReentrantLock();
public static void main(String args[]) throws InterruptedException {
//主线程 拿到锁
lock.lock();
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("begain to get lock...");
//子线程 获取锁(不死不休,直至主线程释放锁后获取到锁为止)
lock.lock();
System.out.println("succeed to get lock...");
}
});
th.start();
Thread.sleep(10000L);
lock.unlock();
}
}
输出结果:
begain to get lock...
#等待10秒后主线程释放锁,子线程拿到锁
succeed to get lock...
tryLock()
-
boolean tryLock()
如果锁被占用则直接跳过
package com.hzw.subject1.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { static Lock lock = new ReentrantLock(); public static void main(String args[]) throws InterruptedException { //主线程 拿到锁 lock.lock(); Thread th = new Thread(new Runnable() { @Override public void run() { System.out.println("begain to get lock..."); //子线程 获取锁(浅尝辄止) boolean result = lock.tryLock(); System.out.println("是否获得到锁:" +result); } }); th.start(); Thread.sleep(10000L); lock.unlock(); } }
输出结果:
begain to get lock... 是否获得到锁:false
-
boolean tryLock(long time, TimeUnit unit)
如果锁被占用则等待一会,如果依旧被占用则直接跳过
package com.hzw.subject1.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { static Lock lock = new ReentrantLock(); public static void main(String args[]) throws InterruptedException { //主线程 拿到锁 lock.lock(); Thread th = new Thread(new Runnable() { @Override public void run() { System.out.println("begain to get lock..."); //子线程 获取锁(点到为止) try { boolean result1 = lock.tryLock(5, TimeUnit.SECONDS); System.out.println("是否获得到锁:" +result1); } catch (InterruptedException e) { e.printStackTrace(); } } }); th.start(); Thread.sleep(10000L); lock.unlock(); } }
输出结果:
begain to get lock... #等待5秒钟 是否获得到锁:false
void lockInterruptibly()
如果锁被占用则会一直等待,直到其他线程释放锁,当前线程获取到锁为止,如果中途收到interrupt()
的信号也会停止等待,抛出执行后面的程序
package com.hzw.subject1.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
static Lock lock = new ReentrantLock();
public static void main(String args[]) throws InterruptedException {
//主线程 拿到锁
lock.lock();
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("begain to get lock...");
//子线程 获取锁(任人摆布)
try {
System.out.println("start to get lock Interruptibly");
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("main asked me to stop...");
}
}
});
th.start();
Thread.sleep(2000L);
th.interrupt();
Thread.sleep(10000L);
lock.unlock();
}
}
输出结果:
begain to get lock...
start to get lock Interruptibly
#等待2秒钟收到interrupt()的中断信号
java.lang.InterruptedException
at com.hzw.subject1.lock.Demo1_GetLock$1.run(LockDemo.java:36)
at java.lang.Thread.run(Thread.java:748)
main asked me to stop...
void unlock()
解锁方法,加锁和解锁必须成对出现,当解锁次数大于加锁次数时会造成死锁,反之会报错
Condition newCondition()
Lock接口的等待池,需要与Lock配合使用,可以提供多个等待池,可以简化代码逻辑,使逻辑更清晰
Object中wait()、notify()、notifyAll()只能和synchronized配合使用,这里只会有一个等待池
- void await():挂起当前线程
- void signal():唤醒当前线程
挂起和唤醒操作必须在获取到锁之后才能执行
示例代码:
package com.hzw.subject1.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo1 { private static Lock lock = new ReentrantLock();
private static Condition con1 = lock.newCondition();
private static Condition con2 = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
System.out.println("condition.await()");
try {
con1.await();
System.out.println("here i am...");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
th.start();
Thread.sleep(2000L);
try {
lock.lock();
con1.signalAll();
// //此处使用con2无法唤醒挂起在con1中的线程
// con2.signalAll();
}finally {
lock.unlock();
}
}
}
ReentrantLock
原理:
加锁:多个线程来进行抢锁,首先会先判断count是否为0,如果count为0则说明锁未被占用,使用CAS操作将其改为1后抢锁成功(假设T1拿到了锁),抢到锁以后将owner改为当前线程(T1);如果count不为0,则说明当前锁已被占用,则先判断占用锁的线程是否为自己,如果是自己,则进行锁重入操作,对count进行加1。如果不是自己,则抢锁失败,进入等待队列中挂起。
解锁:当前线程(T1)使用unLock方法进行解锁操作,此时count减1,判断减1后count的值是否为0,如果count为0,则说明解锁成功,将owner置为null并唤醒等待队列中的一个线程,被解锁的线程重新参与到抢锁的线程队列中进行抢锁;如果count不为0,则说明当前锁是被重入过的,需要继续解锁直到完全释放。
手写ReentrantLock:
package com.hzw.subject1.lock.mylock;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.*;
public class MyReentrantLock implements Lock {
/**
* 标记重入次数的count值
*/
private AtomicInteger count = new AtomicInteger();
/**
* 锁的拥有者
*/
private AtomicReference<Thread> owner = new AtomicReference<>();
/**
* 等待队列
*/
private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();
@Override
public void lock() {
//尝试抢锁
if (!tryLock()) {
//抢锁失败,加入等待队列
waiters.offer(Thread.currentThread());
//自旋
for (; ; ) {
Thread head = waiters.peek();
//如果唤醒的不是队列头,在判定为伪唤醒,让其重新进入park状态
if (Thread.currentThread() == head && tryLock()) {
//唤醒的线程抢到锁以后移除等待队列
waiters.poll();
return;
}
LockSupport.park();
}
}
}
@Override
public boolean tryLock() {
//判断count是否为0,若count != 0 则说明锁已经被占用
int ct = count.get();
if (ct == 0) {
//通过CAS(0, 1)进行抢锁
if (count.compareAndSet(ct, 1)) {
//将owner设为当前线程的引用
owner.set(Thread.currentThread());
return true;
}
} else if(owner.get() == Thread.currentThread()){//判断当前占用锁的线程是否为当前线程
//锁重入,count++
count.getAndIncrement();
return true;
}
//锁已被占用,且不是自己或抢锁失败
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
//当前线程中断状态为true则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//尝试抢锁
if (tryLock())
return true;
//如果等待时间<=0则直接抢锁失败
if (time <= 0)
return false;
//将time转换为纳秒
long nanos = unit.toNanos(time);
//抢锁的最后期限
long deadline = nanos + System.nanoTime();
for (; ; ) {
if (tryLock())
return true;
if (System.nanoTime() - deadline > 0L)
return false;
}
}
@Override
public void unlock() {
//尝试解锁
if (tryUnlock()) {
//解锁成功,唤醒等待队列头部的线程
Thread head = waiters.peek();
if (null != head) {
LockSupport.unpark(head);
}
}
}
private boolean tryUnlock() {
//判断是否是当前线程拿到的锁,如果不是,抛出异常
if (owner.get() != Thread.currentThread())
throw new IllegalMonitorStateException();
//将count减1
int c = count.get() -1;
//判断count是否为0,如果为0,则当前线程的锁全部解完,将owner置空
if (c != 0)
return false;
owner.set(null);
count.set(c);
return true;
}
@Override
public void lockInterruptibly() throws InterruptedException {
//判断线程中断状态
if (Thread.interrupted())
throw new InterruptedException();
//尝试抢锁
if (!tryLock()) {
//抢锁失败,加入等待队列
waiters.offer(Thread.currentThread());
//自旋
for (; ; ) {
Thread head = waiters.peek();
//如果唤醒的不是队列头,在判定为伪唤醒,让其重新进入park状态
if (Thread.currentThread() == head && tryLock()) {
//唤醒的线程抢到锁以后移除等待队列
waiters.poll();
return;
}
//判断线程中断状态
if (Thread.interrupted())
throw new InterruptedException();
LockSupport.park();
}
}
}
@Override
public Condition newCondition() {
return null;
}
}
synchronized与Lock比较
- synchronized
- 优点:使用简单,语义清晰,哪里需要点哪里;由JVM提供,提供了多种优化方案(锁粗化、锁消除、偏向锁、轻量级锁);锁的释放由虚拟机来完成,不用人工干预,降低了造成思索的可能性
- 缺点:无法实行一些锁的高级功能(公平锁、中断锁、超时锁、读写锁、共享锁等)
- Lock
- 优点:所有synchronized的缺点;可以实现更多的功能,让synchronized缺点更多
- 缺点:需要手动释放锁(unlock),使用不当容易造成死锁