Lock接口
在Java 1.5中,官方在concurrent并发包中加入了Lock接口。
public interface Lock {void lock();//加锁//可中断获取锁,与lock()不同之处在于可响应中断操作void lockInterruptibly() throws InterruptedException;//试非阻塞获取锁,调用该方法后立即返回结果,如果能够获取则返回true,否则返回falseboolean tryLock();//根据传入的时间段获取锁,在指定时间内没有获取锁则返回false,如果在指定时间内当前线程未被中并断获取到锁则返回trueboolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();//解锁//获取等待通知组件,该组件与当前锁绑定,当前线程只有获得了锁,才能调用该组件方法Condition newCondition();}
lock方法
lock()方法是无返回值,如果锁已被其他线程获取则进行等待。lock必须主动去释放锁,并且在发生异常时,不会自动释放锁。故使用lock必须在try-catch-finally块中进行,以保证锁一定被被释放,防止死锁的发生。
public class LockTest {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
final LockTest test = new LockTest();
new Thread(){
public void run(){
test.insert(Thread.currentThread());
}
}.start();
new Thread(){
public void run(){
test.insert(Thread.currentThread());
}
}.start();
}
public void insert(Thread thread){
lock.lock();
try {
System.out.println(thread.getName() + "得到了锁");
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName=" + Thread.currentThread().getName() + (" " + (i + 1)));
}
} catch (Exception e) {
} finally {
System.out.println(thread.getName() + "释放了锁");
lock.unlock();
}
}
}
Thread-1得到了锁
ThreadName=Thread-1 1
ThreadName=Thread-1 2
ThreadName=Thread-1 3
ThreadName=Thread-1 4
ThreadName=Thread-1 5
Thread-1释放了锁
Thread-0得到了锁
ThreadName=Thread-0 1
ThreadName=Thread-0 2
ThreadName=Thread-0 3
ThreadName=Thread-0 4
ThreadName=Thread-0 5
Thread-0释放了锁
tryLock方法
tryLock()方法是有返回值的,如果获取锁成功则返回true,如果获取失败则返回false,这个方法无论如何都会立即返回,拿不到锁时也不会一直在那等待。
tryLock(long time, TimeUnit unit)方法拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
public class LockTest {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
LockTest test = new LockTest();
new Thread(()->{test.insert(Thread.currentThread());}).start();
new Thread(()->{test.insert(Thread.currentThread());}).start();
}
public void insert(Thread thread){
if(lock.tryLock()) {
try {
System.out.println(thread.getName()+"得到了锁");
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName=" + Thread.currentThread().getName() + (" " + (i + 1)));
}
} catch (Exception e) {
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
} else {
System.out.println(thread.getName()+"获取锁失败");
}
}
}
Thread-0得到了锁
ThreadName=Thread-0 1
ThreadName=Thread-0 2
ThreadName=Thread-0 3
ThreadName=Thread-0 4
ThreadName=Thread-0 5
Thread-0释放了锁
Thread-1得到了锁
ThreadName=Thread-1 1
ThreadName=Thread-1 2
ThreadName=Thread-1 3
ThreadName=Thread-1 4
ThreadName=Thread-1 5
Thread-1释放了锁
lockInterruptibly方法
lockInterruptibly()无返回值,但是会抛出异常,如果线程正在等待获取锁,则这个线程能中断线程的等待状态。即当两个线程同时通过lockInterruptibly()想获取某个锁时,若线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
当一个线程获取了锁之后,是不会被interrupt()方法中断的。而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。
public class LockTest {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
LockTest test = new LockTest();
MyThread thread1 = new MyThread(test);
MyThread thread2 = new MyThread(test);
thread1.start();
thread2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//两秒后thread2中断
thread2.interrupt();
}
public void insert(Thread thread) throws InterruptedException {
lock.lockInterruptibly();
try {
System.out.println(thread.getName()+"得到了锁");
long startTime = System.currentTimeMillis()/1000;
//超过4s就会释放锁
for (; ;) {
if (System.currentTimeMillis()/1000 - startTime >= 4)
break;
}
} catch (Exception e) {
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
}
}
class MyThread extends Thread {
private LockTest test;
public MyThread(LockTest test) {
this.test = test;
}
@Override
public void run() {
try {
test.insert(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被中断");
}
}
}
Thread-0得到了锁
Thread-1被中断
Thread-0释放了锁
public class LockTest {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
LockTest test = new LockTest();
new Thread(()->{
try {
test.insert(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
test.insert(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
public void insert(Thread thread) throws InterruptedException {
lock.lockInterruptibly();
try {
System.out.println(thread.getName()+"得到了锁");
long startTime = System.currentTimeMillis()/1000;
//超过2s就会释放锁
for (; ;) {
if (System.currentTimeMillis()/1000 - startTime >= 2)
break;
}
} catch (Exception e) {
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
}
}
Thread-1得到了锁
Thread-1释放了锁
Thread-0得到了锁
Thread-0释放了锁
重入锁ReetrantLock
ReetrantLock,JDK 1.5新增的类,实现了Lock接口,作用与synchronized关键字相当,但比synchronized更加灵活。
ReetrantLock本身也是一种支持重进入的锁,即该锁可以支持一个线程对资源重复加锁,同时也支持公平锁与非公平锁。 

public class ReenterLock implements Runnable{
public static ReentrantLock lock=new ReentrantLock();
public static int i=0;
@Override
public void run() {
for(int j=0;j<10000000;j++){
lock.lock();
//支持重入锁
lock.lock();
try{
i++;
}finally{
//执行两次解锁
lock.unlock();
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
ReenterLock tl=new ReenterLock();
Thread t1=new Thread(tl);
Thread t2=new Thread(tl);
t1.start();t2.start();
t1.join();t2.join();
//输出结果:20000000
System.out.println(i);
}
}
Condition
Condition可通过多个Condition实例对象建立更加精细的线程控制。AQS中存在两种队列,一种是同步队列,一种是等待队列,而等待队列就相对于Condition而言的。
使用Condition前必须获得锁,其结点的waitStatus的值为CONDITION,在实现类ConditionObject中有两个结点分别是firstWaiter和lastWaiter。
public class ConditionObject implements Condition, java.io.Serializable {
//等待队列第一个等待结点
private transient Node firstWaiter;
//等待队列最后一个等待结点
private transient Node lastWaiter;
//省略其他代码.......
}
ReentrantReadWriteLock
public class ReadWrite {
public static void main(String[] args) {
// TODO Auto-generated method stub
final MyCache myCache=new MyCache();
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
myCache.write(temp+"", temp+"");
}
},String.valueOf(i)).start();
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
myCache.read(temp+"");
}
},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile Map<String,Object> map=new HashMap<>();
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//新建一个集合 储存读或者写的内容
public void read(String key) {
reentrantReadWriteLock.readLock().lock();
try{
System.out.println(Thread.currentThread().getName()+"\t正在读取");
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object result= map.get(key);
System.out.println(Thread.currentThread().getName()+"\t 读取完成:"+result);
}finally {
reentrantReadWriteLock.readLock().unlock();
}
}
public void write(String key,Object value) {
reentrantReadWriteLock.writeLock().lock();
try{
System.out.println(Thread.currentThread().getName()+"\t正在写入:"+key);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName()+"\t 写入完成!");
}finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
}
3.Synchronized 与Lock
- Synchronized 是Java关键字,属于 JVM 层面,底层是通过 monitorenter 和 monitorexit 完成,依赖于 monitor 对象来完成;Lock是java.util.concurrent.locks.lock,底层是AQS。
- synchronized是不可中断的,而Lock可以是不可中断的也可以是可中断的。
- Synchronized 会自动释放锁,Lock必须要手动加锁和手动释放锁,可能会遇到死锁。
- Synchronized 无法判断获取锁的状态,Lock可以判断。
- synchronized能锁住方法和代码块,而Lock只能锁住代码块。
- Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码。
- synchronized是非公平锁,ReentrantLock可以控制是否是公平锁。
- Lock可以使用读锁提高多线程效率。
- synchronized随机唤醒一个或全部线程,Lock可以精确唤醒。
参考
基于并发AQS的(独占锁)重入锁(ReetrantLock)及其Condition实现原理
