目录
Lock接口的实现类:可重入锁 ReentrantLock 主要方法
lock.tryLock(1000,TimeUnit.SECONDS)
介绍
java jdk1.5之前使用 synchronized 实现锁,1.5之后并发包中新增了Lock接口以及相关实现类来实现锁功能。synchronized方法和语句的范围机制使得使用监视器锁更容易编程,并且有助于避免涉及锁的许多常见编程错误,但是有时您需要以更灵活的方式处理锁。
synchronized 修饰方块执行顺序
- 1.获取锁的线程执行完了该代码块,然后线程释放对锁的占有
- 2.线程执行发生异常,此时JVM会让线程自动释放锁
lock 提供了更加灵活的方式实现
lock普通获取锁
lockInterruptibly可中断锁
tryLock()判断锁释放获取到
ryLock(long time, TimeUnit unit) 获取锁可等待时间,(时间long型,时间类型(毫秒,秒分时等))
unlock 释放锁
newCondition() 条件
复制代码
lock 接口
原码,接口Lock,接口的实现类有 ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
复制代码
这玩意应用场景
- lock 就普通代码块即可
- tryLock 和带参数的 在业务需要有丢弃情况下,有的过时就扔掉,这种,就用这玩意
- lockInterruptibly 这个中断锁,在用的时候,不着急,先干别的线程任务,让那个任务停一会,等的别的线程用完这块了,发个通知,它再去干去,这种场景使用。
- newCondition 条件 它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。
加锁和释放锁示例
lock普通获取锁
Lock lock=new ReentrantLock();
lock.lock();
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
复制代码
tryLock()
Lock lock=new ReentrantLock();
if(lock.tryLock()) {
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}
复制代码
tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回,在拿不到锁时也不会一直在那等待。
中断锁
public void method() throws InterruptedException {
Lock lock=new ReentrantLock();
lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}
复制代码
- 当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。因此当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以响应中断的。而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去
newCondition()
获取等待通知组件,该组件和当前的锁绑定,当前线程只有获得了锁,才能调用该组件的wait()方法,而调用后,当前线程将释放锁。
Lock接口的实现类:可重入锁 ReentrantLock 主要方法
ReentrantLock() //创建一个 ReentrantLock 的实例
ReentrantLock(boolean fair) //创建一个具有给定公平策略的 ReentrantLock
int getHoldCount() //查询当前线程保持此锁的次数
protected Thread getOwner() //返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null
protected Collection<Thread> getQueuedThreads() //返回一个collection,它包含可能正等待获取此锁的线程
int getQueueLength() //返回正等待获取此锁的线程估计数
protected Collection<Thread> getWaitingThreads(Condition condition) //返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程
int getWaitQueueLength(Condition condition) //返回等待与此锁相关的给定条件的线程估计数
boolean hasQueuedThread(Thread thread) //查询给定线程是否正在等待获取此锁
boolean hasQueuedThreads() //查询是否有些线程正在等待获取此锁
boolean hasWaiters(Condition condition) //查询是否有些线程正在等待与此锁有关的给定条件
boolean isFair() //如果此锁的公平设置为 true,则返回true
boolean isHeldByCurrentThread() //查询当前线程是否保持此锁
boolean isLocked() //查询此锁是否由任意线程保持
void lock() //获取锁
void lockInterruptibly() //如果当前线程未被中断,则获取锁。
Condition newCondition() //返回用来与此 Lock 实例一起使用的 Condition 实例
boolean tryLock() //仅在调用时锁未被另一个线程保持的情况下,才获取该锁
boolean tryLock(long timeout, TimeUnit unit) //如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁
void unlock() //试图释放此锁
复制代码
ReentrantLock 锁示例
lock示例
package com.superman.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest2 {
public static void main(String[] args) {
LockTask test = new LockTask();
for (int i = 0; i <6; i++) {//执行6个线程
new LockTaskThread(test).start();
}
}
static class LockTaskThread extends Thread{//调用线程
private LockTask lt;
public LockTaskThread(LockTask lt){
this.lt=lt;
}
public void run(){
lt.insert(Thread.currentThread());
}
}
static class LockTask {//任务类
private Lock lock = new ReentrantLock();
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-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释放了锁
Thread-2得到了锁
ThreadName=Thread-2 1
ThreadName=Thread-2 2
ThreadName=Thread-2 3
ThreadName=Thread-2 4
ThreadName=Thread-2 5
Thread-2释放了锁
Thread-3得到了锁
ThreadName=Thread-3 1
ThreadName=Thread-3 2
ThreadName=Thread-3 3
ThreadName=Thread-3 4
ThreadName=Thread-3 5
Thread-3释放了锁
Thread-4得到了锁
ThreadName=Thread-4 1
ThreadName=Thread-4 2
ThreadName=Thread-4 3
ThreadName=Thread-4 4
ThreadName=Thread-4 5
Thread-4释放了锁
Thread-5得到了锁
ThreadName=Thread-5 1
ThreadName=Thread-5 2
ThreadName=Thread-5 3
ThreadName=Thread-5 4
ThreadName=Thread-5 5
Thread-5释放了锁
复制代码
- 这是一个顺序执行的锁,一个运行完下一个进入
tryLock 锁
static class LockTask {
private Lock lock = new ReentrantLock();
public void insert(Thread thread){
if(lock.tryLock()) {
try {
System.out.println(thread.getName()+"得到了锁");
for (int i = 0; i < 3; 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得到了锁
Thread-3获取锁失败
Thread-1获取锁失败
Thread-2获取锁失败
Thread-4获取锁失败
ThreadName=Thread-0 1
Thread-5获取锁失败
ThreadName=Thread-0 2
ThreadName=Thread-0 3
Thread-0释放了锁
复制代码
lock.tryLock(1000,TimeUnit.SECONDS)
static class LockTaskThread extends Thread{
private LockTask lt;
public LockTaskThread(LockTask lt){
this.lt=lt;
}
public void run(){
try {
lt.insert(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class LockTask {
private Lock lock = new ReentrantLock();
public void insert(Thread thread) throws InterruptedException{
if(lock.tryLock(1000,TimeUnit.SECONDS)) {
try {
System.out.println(thread.getName()+"得到了锁");
for (int i = 0; i < 3; 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()+"获取锁失败");
}
}
}
复制代码
lockInterruptibly()响应中断的使用方法
public class LockTest2 {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
LockTest2 test = new LockTest2();
MyThread thread1 = new MyThread(test);
MyThread thread2 = new MyThread(test);
thread1.start();
thread2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.interrupt();
}
public void insert(Thread thread) throws InterruptedException {
lock.lockInterruptibly(); //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
try {
System.out.println(thread.getName() + "得到了锁");
long startTime = System.currentTimeMillis();
for (; ; ) {
if (System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
break;
//插入数据
}
} finally {
System.out.println(Thread.currentThread().getName() + "执行finally");
lock.unlock();
System.out.println(thread.getName() + "释放了锁");
}
}
static class MyThread extends Thread {
private LockTest2 test;
public MyThread(LockTest2 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被中断
复制代码
Condition接口使用
- synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制,ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。Condition是JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。
- 在使用notify/notifyAll()方法进行通知时,被通知的线程是有JVM选择的,使用ReentrantLock类结合Condition实例可以实现“选择性通知
- synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程
接口的主要实现方法:
void await() //造成当前线程在接到信号或被中断之前一直处于等待状态。
boolean await(long time, TimeUnit unit) //造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
long awaitNanos(long nanosTimeout) //造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
void awaitUninterruptibly() //造成当前线程在接到信号之前一直处于等待状态。
boolean awaitUntil(Date deadline) //造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
void signal() //唤醒一个等待线程。
void signalAll() //唤醒所有等待线程。
复制代码
Condition实现等待/通知机制
public class test1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(3000);
service.signal();
}
static public class MyService {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void await() {
lock.lock();
try {
System.out.println("await时间为:" + System.currentTimeMillis());
condition.await();
System.out.println("这是condition.await()方法之后的语句,condition.signal()方法之后我才被执行");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signal() {
lock.lock();
try {
System.out.println("signal时间为" + System.currentTimeMillis());
condition.signal();
Thread.sleep(3000);
System.out.println("这是condition.signal()方法之后的语句");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
static public class ThreadA extends Thread {
private MyService service;
public ThreadA(MyService service) {
this.service = service;
}
@Override
public void run() {
service.await();
}
}
}
复制代码
必须执行完signal所在的try语句块之后才释放锁,condition.await()后的语句才能被执行。
多个Condition实例实现等待/通知机制
public class test1 {
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
public void awaitA() {
lock.lock();
try {
System.out.println("begin awaitA时间为" + System.currentTimeMillis() + " ThreadName=" + Thread.currentThread().getName());
conditionA.await();
System.out.println("end awaitA时间为" + System.currentTimeMillis() + " ThreadName=" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void awaitB() {
lock.lock();
try {
System.out.println("begin awaitB时间为" + System.currentTimeMillis() + " ThreadName=" + Thread.currentThread().getName());
conditionB.await();
System.out.println("end awaitB时间为" + System.currentTimeMillis() + " ThreadName=" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalAll_A() {
lock.lock();
try {
System.out.println("signalAll_A时间为" + System.currentTimeMillis() + " ThreadName=" + Thread.currentThread().getName());
conditionA.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalAll_B() {
lock.lock();
try {
System.out.println("signalAll_B时间为" + System.currentTimeMillis() + " ThreadName=" + Thread.currentThread().getName());
conditionB.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
test1 service = new test1();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll_A();
}
static public class ThreadA extends Thread {
private test1 service;
public ThreadA(test1 service) {
this.service = service;
}
@Override
public void run() {
service.awaitA();
}
}
static public class ThreadB extends Thread {
private test1 service;
public ThreadB(test1 service) {
this.service = service;
}
@Override
public void run() {
service.awaitB();
}
}
}
复制代码
Condition实现顺序执行
package com.superman.test;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class test1 {
volatile private static int nextPrintWho = 1;
// 默认情况下ReentranLock类使用的是非公平锁
final private static ReentrantLock lock = new ReentrantLock();
final private static Condition conditionA = lock.newCondition();
final private static Condition conditionB = lock.newCondition();
final private static Condition conditionC = lock.newCondition();
public static void main(String[] args) {
Thread threadA = new Thread() {
public void run() {
lock.lock();
try {
while (nextPrintWho != 1) {
conditionA.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadA" + (i + 1));
}
nextPrintWho = 2;
// 通知conditionB实例的线程运行
conditionB.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadB = new Thread() {
public void run() {
lock.lock();
try {
while (nextPrintWho != 2) {
conditionB.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadB" + (i + 1));
}
nextPrintWho = 3;
// 通知conditionB实例的线程运行
conditionC.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadC = new Thread() {
public void run() {
lock.lock();
try {
while (nextPrintWho != 3) {
conditionC.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadC" + (i + 1));
}
nextPrintWho = 1;
// 通知conditionB实例的线程运行
conditionA.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread[] array1 = new Thread[5];
Thread[] array2 = new Thread[5];
Thread[] array3 = new Thread[5];
for (int i = 0; i < 5; i++) {
array1[i] = new Thread(threadA);
array2[i] = new Thread(threadB);
array3[i] = new Thread(threadC);
array1[i].start();
array2[i].start();
array3[i].start();
}
}
}
复制代码
在一个线程运行完之后通过condition.signal()/condition.signalAll()方法通知下一个特定的运行运行,就这样循环往复即可。
ReadWriteLock
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing
*/
Lock writeLock();
}
复制代码
ReentrantReadWriteLock实现了ReadWriteLock接口
ReentrantReadWriteLock接口简介
- 读锁共享,写锁互斥
synchronized 效果
public class test1 {
public static void main(String[] args) {
final test1 test = new test1();
for (int i = 0; i <3; i++) {
new Thread() {
public void run() {
test.get(Thread.currentThread());
}
}.start();
}
}
synchronized public void get(Thread thread) {
for (int i = 0; i < 2; i++) {
System.out.println(thread.getName() + "正在进行读操作");
}
System.out.println(thread.getName() + "读操作完毕");
}
}
复制代码
只有执行完第一个才会执行第二个任务
实现读写锁 读读共享
public class test1 {
public static void main(String[] args) {
final test1 test = new test1();
for (int i = 0; i < 3; i++) {
new Thread() {
public void run() {
test.read(Thread.currentThread());
}
}.start();
}
}
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public void read(Thread thread) {
rwl.readLock().lock();
try {
for (int i = 0; i < 3; i++) {
System.out.println(thread.getName() + "正在进行读操作");
}
System.out.println(thread.getName() + "读操作完毕");
} finally {
rwl.readLock().unlock();
}
}
}
复制代码
同时进行读取,实现了读读共享
读写锁 写写互斥
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class test1 {
public static void main(String[] args) {
final test1 test = new test1();
for (int i = 0; i < 3; i++) {
new Thread() {
public void run() {
test.write(Thread.currentThread());
}
}.start();
}
}
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public void write(Thread thread) {
rwl.writeLock().lock();
try {
for (int i = 0; i < 3; i++) {
System.out.println(thread.getName() + "正在进行写操作");
}
System.out.println(thread.getName() + "写操作完毕");
} finally {
rwl.writeLock().unlock();
}
}
}
复制代码
写写相斥,只有执行完上一个才能执行下一个
总结
可重入锁
如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,方法1 调用方法2 同一个类中,都是synchronized 修改时,方法2不用从新申请锁。
可中断锁
在Java中,synchronized就不是可中断锁,而Lock是可中断锁。如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。
公平锁
公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。
在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。设置 构造方法传入 false 就是 公平锁,true 就是非公平锁,默认也是非公平锁
读写锁
读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。
ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。可以通过readLock()获取读锁,通过writeLock()获取写锁。
看下加不加锁的区别
加锁
package com.superman.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class testV {
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
System.out.println("start");
a1 a = new a1();
for (int i = 0; i < 5; i++) {
final int now = i;
new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
a.setName(now+"task");
a.execute();
try {
Thread.sleep(12);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}
}).start();
}
System.out.println("end");
}
}
class a1 {
private String name = "";
public void setName(String name){this.name=name;}
private Lock lock = new ReentrantLock();
public void execute(){
lock.lock();
for (int i = 0; i <5; i++) {
System.out.println(name+" run :"+i);
}
lock.unlock();
}
}
复制代码
加锁 内部变量 name 都出现了没有被替换掉ok
不加锁
package com.superman.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class testV {
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
System.out.println("start");
a1 a = new a1();
for (int i = 0; i < 5; i++) {
final int now = i;
new Thread(new Runnable() {
@Override
public void run() {
// lock.lock();
a.setName(now+"task");
a.execute();
try {
Thread.sleep(12);
} catch (InterruptedException e) {
e.printStackTrace();
}
// lock.unlock();
}
}).start();
}
System.out.println("end");
}
}
class a1 {
private String name = "";
public void setName(String name){this.name=name;}
private Lock lock = new ReentrantLock();
public void execute(){
lock.lock();
for (int i = 0; i <5; i++) {
System.out.println(name+" run :"+i);
}
lock.unlock();
}
}
复制代码
不加锁,内部便利name 等于 0 的值被替换掉了,后边之前大量换成3 这就是,加锁和不加锁的区别,有点生猛哈
ok
持续更新