1.相比synchronized,ReentrantLock(重入锁)增加了一些高级功能
等待可中断——对于synchronized,如果一个线程正在等待锁,那么结果只有两种情况,要么获得这把锁继续执行 ,要么就保持等待。而使用ReentrantLock,如果一个线程正在等待锁,那么它依然可以收到通知,被告知无需再等待,可以停止工作了。
1)lockInterruptiby()的使用:
线程t1一直占用者锁 不释放,此时t2一直在等待着锁,通过lockInterruptibly()方法可以通知线程t2停止等待,而使用synchronized方式,只能继续等待。注意:这里说的是一个线程正在等待锁即没有获得锁的情况,而不是说获得锁后执行出行异常,这两者不一样,因为synchronized同步的代码出现异常,也会释放锁,例如在synchronized同步代码中进入sleep(),此时通过中断,也会释放锁。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
public class ReentrantLock4 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(()->{
try {
lock.lock();
System.out.println("t1 start");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
System.out.println("t1 end");
} catch (InterruptedException e) {
System.out.println("interrupted!");
} finally {
lock.unlock();
}
});
t1.start();
Thread t2 = new Thread(()->{
try {
lock.lockInterruptibly(); //可以对interrupt()方法做出响应
System.out.println("t2 start");
TimeUnit.SECONDS.sleep(5);
System.out.println("t2 end");
} catch (InterruptedException e) {
System.out.println("interrupted!"); //在这里处理退出中断后的后续动作
} finally {
if(lock.isHeldByCurrentThread())
{
lock.unlock();
}
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt(); //打断线程2的等待
}
}
t1 start
interrupted!
2)使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行,可以根据tryLock的返回值来判定是否锁定
也可以指定tryLock的时间,犹如tryLock(time)抛出异常,所以要注意unlock的处理,必须方法finally中。
代码示例:
class MyService {
public ReentrantLock lock = new ReentrantLock();
public void waitMethod() {
if (lock.tryLock()) { //如果获取到了锁,打印"获得锁",否则打印"没有获得锁"
System.out.println(Thread.currentThread().getName() + "获得锁");
} else {
System.out.println(Thread.currentThread().getName() + "没有获得锁");
}
}
}
public class HighConcurrency {
public static void main(String[] args) throws InterruptedException {
final MyService service = new MyService();
Runnable runnableRef = new Runnable() {
@Override
public void run() {
service.waitMethod();
}
};
Thread threadA = new Thread(runnableRef);
threadA.setName("A");
threadA.start();
Thread threadB = new Thread(runnableRef);
threadB.setName("B");
threadB.start();
}
}
B没有获得锁
A获得锁
tryLock(time)示例:
class MyService {
public ReentrantLock lock = new ReentrantLock();
public void waitMethod() {
try {
if (lock.tryLock(3, TimeUnit.SECONDS)) { //等待3s后,如果没有获取锁,则执行else里语句
System.out.println(" " + Thread.currentThread().getName()
+ "获得锁的时间:" + System.currentTimeMillis());
Thread.sleep(10000);
} else {
System.out.println(" " + Thread.currentThread().getName()
+ "没有获得锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
public class HighConcurrency {
public static void main(String[] args) throws InterruptedException {
final MyService service = new MyService();
Runnable runnableRef = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ "调用waitMethod时间:" + System.currentTimeMillis());
service.waitMethod();
}
};
Thread threadA = new Thread(runnableRef);
threadA.setName("A");
threadA.start();
Thread threadB = new Thread(runnableRef);
threadB.setName("B");
threadB.start();
}
}
A调用waitMethod时间:1526127191925
B调用waitMethod时间:1526127191925
A获得锁的时间:1526127191926
B没有获得锁
说明A先获取锁,B等待3S后没有获取锁,就进入else语句执行,不再等待。
2.可实现公平锁:公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁,而非公平锁不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁,synchronized中的锁是非公平的,ReenrantLock默认情况下也是非公平的,但是可以在构造函数中设置为公平锁。ReentrantLock(boolean fair)
。
公平锁示例:
public class ReentrantLock5 extends Thread {
private static ReentrantLock lock=new ReentrantLock(true); //参数为true表示为公平锁
public void run() {
for(int i=0; i<100; i++) {
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"获得锁");
}finally{
lock.unlock();
}
}
}
public static void main(String[] args) {
ReentrantLock5 rl=new ReentrantLock5();
Thread th1=new Thread(rl);
Thread th2=new Thread(rl);
th1.start();
th2.start();
}
}
输出:
Thread-1获得锁
Thread-2获得锁
Thread-1获得锁
Thread-2获得锁
Thread-1获得锁
如果将构造函数中的true去掉,此时成为非公平锁后的打印为:
Thread-1获得锁
Thread-1获得锁
Thread-1获得锁
Thread-1获得锁
3.锁可以绑定多个条件
一个ReentrantLock对象可以同时绑定多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性地进行线程通知。
自己对condtion的理解:condition使得调用该方法的线程进入等待队列,但是唤醒的时候唤醒的是对方的等待队列。用一个例子来说明,有一个容器,有多个生产者和多个消费者,希望当容器满的时候,能够只让生产的线程等待,只唤醒消费的线程,同样当容器空的时候,让消费的线程等待,只唤醒生产的线程。
代码实现:
public class MyContainer2 {
final private LinkedList lists = new LinkedList<>();
final private int MAX = 10; //最多10个元素
private int count = 0;
private Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();
public void put(T t) {
try {
lock.lock();
while(lists.size() == MAX) {
producer.await(); //当消费者线程们执行到这里的时候等待
}
lists.add(t);
++count;
System.out.println("里面有个数:"+count);
consumer.signalAll(); //通知消费者线程进行消费
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public T get() {
T t = null;
try {
lock.lock();
while(lists.size() == 0) {
consumer.await(); //当生产者线程执行到这里的等待
}
t = lists.removeFirst();
count --;
System.out.println("里面有个数:"+count);
producer.signalAll(); //通知生产者的线程们进行生产
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return t;
}
public static void main(String[] args) {
MyContainer2 c = new MyContainer2<>();
//启动消费者线程
for(int i=0; i<10; i++) {
new Thread(()->{
for(int j=0; j<5; j++)
c.get();
//System.out.println(c.get());
}, "c" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//启动生产者线程
for(int i=0; i<2; i++) {
new Thread(()->{
for(int j=0; j<25; j++)
c.put(Thread.currentThread().getName() + " " + j);
}, "p" + i).start();
}
}
}
说明:该代码有一个注意点,在put方法中判断容器是否满的时候使用的是while(lists.size()==MAX),这里能否改成if(lists.size==MAX)呢?这里假设这样一种情形,此时容器已经满了,两个消费者线程都进入await()等待,此时一个消费者线程消费了一个商品,同时唤醒了这两个生产者线程c1和c2,此时被唤醒c1先抢到锁,直接执行wait()后面的语句,生产了一个商品,然后c2抢到锁,此时c2直接从wait()后面开始执行,并不会判断容器是否已经满了,所以导致容器装不下,导致错误。
4.线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态称为阻塞状态。但是阻塞在Lock接口的线程状态却是等待状态,因为Lock接口对于阻塞的实现均使用了LockSupport类中的相关方法。(Java并发编程的艺术90页)
https://blog.csdn.net/chenchaofuck1/article/details/51045134
https://blog.csdn.net/zheng548/article/details/54426947