lock锁的使用
ReentrantLock锁的使用
在JDK5.0版本之前,重入锁的性能远远好于synchronized关键字,JDK6.0版本之后synchronized 得到了大量的优化,二者性能也不分伯仲,但是重入锁是可以完全替代synchronized关键字的。除此之外,重入锁又自带一系列其他功能:可中断响应、锁申请等待限时、公平锁。另外可以结合Condition来使用,使其更是逼格满满。
请看如下使用代码
public class ReenTrantLock {
private Lock lock = new ReentrantLock();
public void testLock() {
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
lock.unlock();
}
}
/**
* @program: demo
* @description: demo
* @author: lee
* @create: 2018-09-03
**/
public class ThreadA extends Thread {
private ReenTrantLock reenTrantLock ;
public ThreadA(ReenTrantLock reenTrantLock) {
this.reenTrantLock = reenTrantLock;
}
@Override
public void run() {
super.run();
this.reenTrantLock.testLock();
}
}
public class Run {
public static void main(String[] args) {
ReenTrantLock reenTrantLock = new ReenTrantLock();
ThreadA threadA = new ThreadA(reenTrantLock);
threadA.start();
ThreadA threadB = new ThreadA(reenTrantLock);
threadB.start();
ThreadA threadC = new ThreadA(reenTrantLock);
threadC.start();
ThreadA threadD = new ThreadA(reenTrantLock);
threadD.start();
}
}
输出结果
Thread-0---0
Thread-0---1
Thread-0---2
Thread-0---3
Thread-0---4
Thread-0---5
Thread-0---6
Thread-0---7
Thread-0---8
Thread-0---9
Thread-1---0
Thread-1---1
Thread-1---2
Thread-1---3
Thread-1---4
Thread-1---5
Thread-1---6
Thread-1---7
Thread-1---8
Thread-1---9
...
ReentrantLock同步实现
请看如下代码
/**
* @program: demo
* @description: demo
* @author: lee
* @create: 2018-09-03
**/
public class ReenTrantLock {
private Lock lock = new ReentrantLock();
public void testLock() {
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
lock.unlock();
}
public void testLockB() {
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
lock.unlock();
}
}
/**
* @program: demo
* @description: demo
* @author: lee
* @create: 2018-09-03
**/
public class ThreadA extends Thread {
private ReenTrantLock reenTrantLock ;
public ThreadA(ReenTrantLock reenTrantLock) {
this.reenTrantLock = reenTrantLock;
}
@Override
public void run() {
super.run();
this.reenTrantLock.testLock();
}
}
public class ThreadB extends Thread {
private ReenTrantLock reenTrantLock ;
public ThreadB(ReenTrantLock reenTrantLock) {
this.reenTrantLock = reenTrantLock;
}
@Override
public void run() {
super.run();
this.reenTrantLock.testLockB();
}
}
public class Run {
public static void main(String[] args) {
ReenTrantLock reenTrantLock = new ReenTrantLock();
ThreadA threadA = new ThreadA(reenTrantLock);
threadA.start();
ThreadB threadB = new ThreadB(reenTrantLock);
threadB.start();
}
}
输出结果
Thread-0---0
Thread-0---1
Thread-0---2
Thread-0---3
Thread-0---4
Thread-0---5
Thread-0---6
Thread-0---7
Thread-0---8
Thread-0---9
Thread-1---0
Thread-1---1
Thread-1---2
Thread-1---3
Thread-1---4
Thread-1---5
Thread-1---6
Thread-1---7
Thread-1---8
Thread-1---9
由输出结果可知,线程之间呈现互斥访问,即lock锁持有的是对象监视器,同一个锁修饰的线程之间的代码是互斥的。
lock锁实现等待通知
在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.concurrent.locks.ReentrantLock 锁,JDK也为我们提供了与此功能相应的类java.util.concurrent.locks.Condition。Condition与重入锁是通过lock.newCondition()方法产生一个与当前重入锁绑定的Condtion实例,我们通知该实例来控制线程的等待与通知。该接口的所有方法:
public interface Condition {
//使当前线程加入 await() 等待队列中,并释放当锁,当其他线程调用signal()会重新请求锁。与Object.wait()类似。
void await() throws InterruptedException;
//调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。
//调用该方法后,结束等待的唯一方法是其它线程调用该条件对象的signal()或signalALL()方法。等待过程中如果当前线程被中断,该方法仍然会继续等待,同时保留该线程的中断状态。
void awaitUninterruptibly();
// 调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。
//nanosTimeout指定该方法等待信号的的最大时间(单位为纳秒)。若指定时间内收到signal()或signalALL()则返回nanosTimeout减去已经等待的时间;
//若指定时间内有其它线程中断该线程,则抛出InterruptedException并清除当前线程的打断状态;若指定时间内未收到通知,则返回0或负数。
long awaitNanos(long nanosTimeout) throws InterruptedException;
//与await()基本一致,唯一不同点在于,指定时间之内没有收到signal()或signalALL()信号或者线程中断时该方法会返回false;其它情况返回true。
boolean await(long time, TimeUnit unit) throws InterruptedException;
//适用条件与行为与awaitNanos(long nanosTimeout)完全一样,唯一不同点在于它不是等待指定时间,而是等待由参数指定的某一时刻。
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一个在 await()等待队列中的线程。与Object.notify()相似
void signal();
//唤醒 await()等待队列中所有的线程。与object.notifyAll()相似
void signalAll();
}
请看实际小程序
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void testLock() {
try {
lock.lock();
System.out.println("即将进入wait唤醒");
condition.await();
System.out.println("wait唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void testLockB() {
try {
lock.lock();
System.out.println("即将唤醒线程");
condition.signal();
}finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread {
private MyService reenTrantLock ;
public ThreadA(MyService reenTrantLock) {
this.reenTrantLock = reenTrantLock;
}
@Override
public void run() {
super.run();
this.reenTrantLock.testLock();
}
}
public class ThreadB extends Thread {
private MyService reenTrantLock ;
public ThreadB(MyService reenTrantLock) {
this.reenTrantLock = reenTrantLock;
}
@Override
public void run() {
super.run();
this.reenTrantLock.testLockB();
}
}
public class Run {
public static void main(String[] args) {
try {
MyService service = new MyService();
ThreadA threadA = new ThreadA(service);
threadA.start();
Thread.sleep(1000);
ThreadB threadB = new ThreadB(service);
threadB.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果
即将进入wait唤醒
即将唤醒线程
wait唤醒
通知部分线程
请看如下代码
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private Condition condition2 = lock.newCondition();
public void testLock() {
try {
lock.lock();
System.out.println("condition即将进入wait唤醒");
condition.await();
System.out.println("condition wait已经唤醒唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void testLockB() {
try {
lock.lock();
System.out.println("condition2即将进入wait唤醒");
condition2.await();
System.out.println("condition2 wait已经唤醒唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void callCondition() {
try {
lock.lock();
condition.signal();
System.out.println("即将唤醒Condition");
}finally {
lock.unlock();
}
}
public void callCondition2() {
try {
lock.lock();
condition2.signal();
System.out.println("即将唤醒Condition2");
}finally {
condition2.signal();
}
}
}
public class ThreadA extends Thread {
private MyService reenTrantLock ;
public ThreadA(MyService reenTrantLock) {
this.reenTrantLock = reenTrantLock;
}
@Override
public void run() {
super.run();
this.reenTrantLock.testLock();
}
}
public class ThreadB extends Thread {
private MyService reenTrantLock ;
public ThreadB(MyService reenTrantLock) {
this.reenTrantLock = reenTrantLock;
}
@Override
public void run() {
super.run();
this.reenTrantLock.testLockB();
}
}
public class Run {
public static void main(String[] args) {
try {
MyService service = new MyService();
ThreadA threadA = new ThreadA(service);
threadA.start();
ThreadB threadB = new ThreadB(service);
threadB.start();
Thread.sleep(1000);
service.callCondition();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果
condition即将进入wait唤醒
condition2即将进入wait唤醒
即将唤醒Condition
condition wait已经唤醒唤醒
此时程序仍未结束,还在处于运行状态,因为其中一个线程还在处于等待状态。
lock实现单生产者单消费着模式
请看如下代码
public class Product {
public static boolean hasValue =false;
}
public class MyService {
private Lock lock = new ReentrantLock();
private boolean hasValue = false;
private Condition condition = lock.newCondition();
public void product() {
try {
lock.lock();
while (true) {
if (Product.hasValue) {
condition.await();
}
System.out.println("生产了一个物品");
Product.hasValue = true;
condition.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void consume() {
try {
lock.lock();
while (true) {
if (!Product.hasValue) {
condition.await();
}
System.out.println("消费了一个物品");
Product.hasValue = false;
condition.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread {
private MyService reenTrantLock ;
public ThreadA(MyService reenTrantLock) {
this.reenTrantLock = reenTrantLock;
}
@Override
public void run() {
super.run();
this.reenTrantLock.product();
}
}
public class ThreadB extends Thread {
private MyService reenTrantLock ;
public ThreadB(MyService reenTrantLock) {
this.reenTrantLock = reenTrantLock;
}
@Override
public void run() {
super.run();
this.reenTrantLock.consume();
}
}
输出结果
生产了一个物品
消费了一个物品
生产了一个物品
消费了一个物品
生产了一个物品
消费了一个物品
生产了一个物品
消费了一个物品
生产了一个物品
消费了一个物品
....
公平锁和非公平锁
公平锁和非公平锁
公平锁的意思就是线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出的方式,并不一定全部是。非公平锁就是指线程随机获取到锁,先加锁的不一定先获得锁,这种方式可能造成一些线程总是拿不到锁,结果也就是不公平的。
请看如下代码
//final MyService service = new MyService(true); 非公平锁
public class MyService {
private ReentrantLock lock ;
private boolean isFair;
public MyService(boolean isFair) {
this.isFair = isFair;
lock = new ReentrantLock(this.isFair);
}
public void serviceMethod() {
try {
lock.lock();
System.out.println("获得锁的线程名称是" + Thread.currentThread().getName());
} finally {
lock.unlock();
}
}
}
public class Run {
public static void main(String[] args) {
final MyService service = new MyService(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("当前线程是" + Thread.currentThread().getName());
service.serviceMethod();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(runnable);
}
for (int i=0;i<10;i++){
threads[i].start();
}
}
}
输出结果
当前线程是Thread-0
获得锁的线程名称是Thread-0
当前线程是Thread-1
获得锁的线程名称是Thread-1
当前线程是Thread-2
获得锁的线程名称是Thread-2
当前线程是Thread-3
获得锁的线程名称是Thread-3
当前线程是Thread-4
获得锁的线程名称是Thread-4
当前线程是Thread-5
获得锁的线程名称是Thread-5
当前线程是Thread-6
获得锁的线程名称是Thread-6
当前线程是Thread-7
当前线程是Thread-8
获得锁的线程名称是Thread-7
获得锁的线程名称是Thread-8
当前线程是Thread-9
获得锁的线程名称是Thread-9
获得的线程基本呈现有序状态
来源:oschina
链接:https://my.oschina.net/u/3798913/blog/1944280