文章目录
ReentrantLock类
ReentrantLock类的使用
调用ReentrantLock对象的 lock()方法获取锁,调用unlock()方法释放锁。Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象。
package multiply.com.test;
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
ThreadAA aa = new ThreadAA(service);
a.setName("A");
aa.setName("AA");
a.start();
aa.start();
}
}
package multiply.com.test;
public class ThreadA extends Thread {
private MyService service;
public ThreadA(MyService service) {
this.service = service;
}
@Override
public void run() {
super.run();
service.methodA();
}
}
package multiply.com.test;
public class ThreadAA extends Thread {
private MyService service;
public ThreadAA(MyService service) {
this.service = service;
}
@Override
public void run() {
service.methodA();
}
}
package multiply.com.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
public void methodA() {
try {
lock.lock();
System.out.println("method A begin Thread name = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("method A end Thread name = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
method A begin Thread name = A time = 1635413024078
method A end Thread name = A time = 1635413029079
method A begin Thread name = AA time = 1635413029080
method A end Thread name = AA time = 1635413034081
使用Condition实现等待|通知
报错的异常信息是监视器出错,解决的办法是必须在condition.await()方法(相当于wait方法)调用之前调用lock.lock()代码获得同步监视器。
package multiply.com.test;
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
ThreadB b = new ThreadB(service);
a.setName("A");
b.setName("B");
a.start();
b.start();
Thread.sleep(3000);
service.signalAll_A();
}
}
package multiply.com.test;
public class ThreadA extends Thread {
private MyService service;
public ThreadA(MyService service) {
this.service = service;
}
@Override
public void run() {
super.run();
service.awaitA();
}
}
package multiply.com.test;
public class ThreadB extends Thread {
private MyService service;
public ThreadB(MyService service) {
this.service = service;
}
@Override
public void run() {
super.run();
service.awaitB();
}
}
package multiply.com.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA time = " + System.currentTimeMillis() + " thread name : " + Thread.currentThread().getName());
conditionA.await();
System.out.println(" end awaitA time = " + System.currentTimeMillis() + " thread name : " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB time = " + System.currentTimeMillis() + " thread name : " + Thread.currentThread().getName());
conditionB.await();
System.out.println(" end awaitB time = " + System.currentTimeMillis() + " thread name : " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalAll_A() {
try {
lock.lock();
System.out.println("signalAll_A time = " + System.currentTimeMillis() + " thread name = " + Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
public void signalAll_B(){
try {
lock.lock();
System.out.println("signalAll_B time = " + System.currentTimeMillis() + " thread name = " + Thread.currentThread().getName());
conditionB.signalAll();
}finally {
lock.unlock();
}
}
}
begin awaitB time = 1635425117297 thread name : B
begin awaitA time = 1635425117298 thread name : A
signalAll_A time = 1635425120297 thread name = main
end awaitA time = 1635425120297 thread name : A
通过此实验可以得知,使用ReentrantLock对象可以唤醒指定种类的线程,这是控制部分线程行为的方便方式。
生产者和消费者模式
package multiply.com.test;
public class Run {
public static void main(String[] args) {
MyService service = new MyService();
ThreadA[] aThread = new ThreadA[10];
ThreadB[] bThread = new ThreadB[10];
for (int i = 0; i < 10; i++) {
aThread[i] = new ThreadA(service);
bThread[i] = new ThreadB(service);
aThread[i].start();
bThread[i].start();
}
}
}
package multiply.com.test;
public class ThreadA extends Thread {
private MyService service;
public ThreadA(MyService service) {
this.service = service;
}
@Override
public void run() {
super.run();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
service.set();
}
}
}
package multiply.com.test;
public class ThreadB extends Thread {
private MyService service;
public ThreadB(MyService service) {
this.service = service;
}
@Override
public void run() {
super.run();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
service.get();
}
}
}
package multiply.com.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
private Condition conditionB = lock.newCondition();
private Condition conditionA = lock.newCondition();
private boolean hasValue = false;
public void set() {
try {
lock.lock();
while (hasValue) {
System.out.println("有可能1连续");
conditionA.await();
}
System.out.println("打印1");
hasValue = true;
conditionB.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (!hasValue) {
System.out.println("有可能2连续");
conditionB.await();
}
System.out.println("打印2");
hasValue = false;
conditionA.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
原因是程序中使用了一个Condition对象,再结合signalAll()方法来唤醒所有的线程,那么唤醒的线程就有可能是同类,所以就出现连续打印“有可能★★连续”或“有可能☆☆连续”的情况了。
公平锁与非公平锁
公平与非公平锁:锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。
lock = new ReentrantLock(false);
1)方法 int getHoldCount()的作用是查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。
2)方法int getQueueLength()的作用是返回正等待获取此锁定的线程估计数,比如有5个线程,1个线程首先执行await()方法,那么在调用getQueueLength()方法后返回值是4,说明有4个线程同时在等待lock 的释放。
3)方法int getWaitQucueLength(Condition condition)的作用是返回等待与此锁定相关的给定条件Condition的线程估计数,比如有5个线程,每个线程都执行了同一个condition对象的await)方法,则调用getWaitQueueLength(Conditioncondition)方法时返回的int值是5。
1)方法 boolean hasQueuedThread(Thread thread)的作用是查询指定的线程是否正在等待获取此锁定。
2)方法 boolean hasWaiters(Condition condition)的作用是查询是否有线程正在等待与此锁定有关的condition条件。
1)方法boolean isFair()的作用是判断是不是公平锁。在默认的情况下,ReentrantLock类使用的是非公平锁。
2)方法 boolean isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定。
3)方法boolean isLocked()的作用是查询此锁定是否由任意线程保持。
1)方法 void lockInterruptibly()的作用是:如果当前线程未被中断,则获取锁定
2)方法 boolean tryLock()的作用是,仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。
3)方法 boolean tryLock(long timeout, TimeUnit unit)的作用是,如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
使用ReentrantReadWriteLock类
读写锁表示也有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。
读读共享
package multiply.com.test;
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
ThreadB b = new ThreadB(service);
b.setName("B");
a.start();
b.start();
}
}
package multiply.com.test;
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.read();
}
}
package multiply.com.test;
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.read();
}
}
package multiply.com.test;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
try {
try {
lock.readLock().lock();
System.out.println("获得读锁" + Thread.currentThread().getName()
+ " " + System.currentTimeMillis());
Thread.sleep(10000);
} finally {
lock.readLock().unlock();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
从控制台中打印的时间来看,两个线程几乎同时进入lock()方法后面的代码。说明在此使用了lock.readLock()读锁可以提高程序运行效率,允许多个线程同时执行lock()方法后面的代码。
写写互斥
使用写锁代码 lockwrieLock)的效果就是同一时间只允许一个线程执行lock()方法后面的代码。
同时读写互斥、写读互斥。