java多线程中,ReentrantLock和synchronized关键字都可以实现线程之间的同步,但是ReentrantLock的功能比synchronized功能更加强大。
默认是非公平锁,传入参数true是公平锁
公平锁:采用先到先得的策略,每次获取锁之前,都会检查队列里边有没有排队等待的线程,没有就会尝试获取锁,如果有就将当前线程添加到等待队列。
非公平锁: 采用有机会插队的策略,一个线程获取锁之前,先尝试获取锁,而不是在线程队列等待,如果获取锁成功,那么线程就成功启动,没有获取到锁,就到等待队列等待。
ReentrantLock lock new ReentrantLock(tag);
public class Lock2Main {
public static void main(String[] args) throws InterruptedException {
testServiceA();
}
public static void testServiceA() throws InterruptedException {
ServiceA serviceA = new ServiceA(true);
ThreadA threadA = new ThreadA(serviceA);
ThreadA threadb = new ThreadA(serviceA);
threadA.start();
threadb.start();
Thread.sleep(100);
}
}
class ThreadA extends Thread {
private ServiceA serviceA;
public ThreadA(ServiceA serviceA) {
this.serviceA = serviceA;
}
@Override
public void run() {
serviceA.testMeth();
}
}
class ServiceA {
ReentrantLock lock;
public ServiceA(boolean tag) {
this.lock = new ReentrantLock(tag);
}
public void testMeth() {
lock.lock();
System.out.println(Thread.currentThread().getName() + "+++++上锁: testMeth()");
testMeth1();
System.out.println(Thread.currentThread().getName() + ": running");
lock.unlock();
System.out.println(Thread.currentThread().getName() + "-----解锁 testMeth()");
}
public void testMeth1() {
lock.lock();
System.out.println(Thread.currentThread().getName() + "+++++上锁: testMeth1()");
System.out.println(Thread.currentThread().getName() + ": running");
lock.unlock();
System.out.println(Thread.currentThread().getName() + "-----解锁 testMeth1()");
}
}
main方法执行结果
Thread-0+++++上锁: testMeth()
Thread-0+++++上锁: testMeth1()
Thread-0: running
Thread-0-----解锁 testMeth1()
Thread-0: running
Thread-0-----解锁 testMeth()
Thread-1+++++上锁: testMeth()
Thread-1+++++上锁: testMeth1()
Thread-1: running
Thread-1-----解锁 testMeth1()
Thread-1: running
Thread-1-----解锁 testMeth()
Process finished with exit code 0
private ReentrantLock lock = new ReentrantLock(); // 加锁
private Condition condition = lock.newCondition(); // 定向唤醒线程
通过Condition来让当前线程等待(await方法),和通知线程就绪(signal方法),但是在在调用await方法之前必须获取锁,即调用lock.lock()方法,否则会抛出异常。
样例代码
public class Lock2Main {
public static void main(String[] args) throws InterruptedException {
testServiceC();
}
public static void testServiceC() throws InterruptedException {
ServiceC ServiceC = new ServiceC();
ThreadC threadC = new ThreadC(ServiceC);
ThreadC1 threadC1 = new ThreadC1(ServiceC);
threadC1.start();
threadC.start();
Thread.sleep(5000);
ServiceC.singleA();
ServiceC.singleB();
}
}
class ThreadC extends Thread {
private ServiceC serviceB;
public ThreadC(ServiceC serviceB) {
this.serviceB = serviceB;
}
@Override
public void run() {
serviceB.awaitB();
}
}
class ThreadC1 extends Thread {
private ServiceC serviceB;
public ThreadC1(ServiceC serviceB) {
this.serviceB = serviceB;
}
@Override
public void run() {
serviceB.awaitA();
}
}
class ServiceC {
private ReentrantLock lock = new ReentrantLock();
private Condition condition_A = lock.newCondition();
private Condition condition_B = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "--begin await--" + System.currentTimeMillis());
condition_A.await();
System.out.println(Thread.currentThread().getName() + "--end await--" + System.currentTimeMillis());
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "--begin await--" + System.currentTimeMillis());
condition_B.await();
System.out.println(Thread.currentThread().getName() + "--end await--" + System.currentTimeMillis());
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
public void singleA() {
try {
lock.lock();
System.out.println("开始调用single方法--" + System.currentTimeMillis());
condition_A.signalAll();
} finally {
lock.unlock();
}
}
public void singleB() {
try {
lock.lock();
System.out.println("开始调用single方法--" + System.currentTimeMillis());
condition_B.signalAll();
} finally {
lock.unlock();
}
}
}
main运行结果
Thread-0--begin await--1696480662130
Thread-1--begin await--1696480662130
开始调用single方法--1696480667134
开始调用single方法--1696480667134
Thread-1--end await--1696480667134
Thread-0--end await--1696480667134
Process finished with exit code 0
调用condition.await();会释放当前的锁。
//和notify与notifyAll方法一样
condition.signal();
condition.signalAll();
一个lock可以创建多个Condition,同一个condition的notify、notifyAll和wait也是搭配使用的,同一个condition.notify()的只能唤醒同一个condition的await()。
wait的一些特性:
wait是可以被中断的(interrupt方法),会抛出InterruptedException异常
condition.waitUninterruptibly(); 不会被打断,线程调用interrupt方法后,方法不会被打断,且不抛出InterruptedException异常
多个类似的wait方法
long awaitNanos(long nanosTimeout) throws InterruptedException; //具有自动唤醒功能,单位纳秒
boolean await(long time, TimeUnit unit) throws InterruptedException; //具有自动唤醒功能,可以指定时间单位
boolean awaitUntil(Date deadline) throws InterruptedException; //在指定日期结束等待
ReentrantLock具有完全排他的特性,同一时间只有一个线程在执行lock方法后的代码,虽然保证了线程的安全,但是效率非常低。ReentrantReadWriteLock在执行读操作时可以同时读,而不执行同步操作。
读写锁有两个锁,读锁和写锁。
读读不互斥,读写互斥,写写互斥。
public class LockReadWrite {
public static void main(String[] args) throws InterruptedException {
System.out.println("写锁测试+++++++++");
ServiceD serviceD = new ServiceD();
ThreadD threadD = new ThreadD(serviceD);
ThreadE threadD1 = new ThreadE(serviceD);
threadD.start();
threadD1.start();
Thread.sleep(10000);
System.out.println("读锁测试--------");
ThreadD threadD2 = new ThreadD(serviceD);
ThreadD threadD3 = new ThreadD(serviceD);
threadD2.start();
threadD3.start();
}
}
class ThreadD extends Thread{
ServiceD serviceD;
public ThreadD(ServiceD serviceD){
this.serviceD = serviceD;
}
@Override
public void run() {
serviceD.readContext();
}
}
class ThreadE extends Thread{
ServiceD serviceD;
public ThreadE(ServiceD serviceD){
this.serviceD = serviceD;
}
@Override
public void run() {
serviceD.writeLock();
}
}
class ServiceD{
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void readContext(){
try{
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "正在读......" + System.currentTimeMillis());
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.readLock().unlock();
System.out.println(Thread.currentThread().getName() + "读退出------" + System.currentTimeMillis());
}
}
public void writeLock(){
try{
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "正在写++++++" + System.currentTimeMillis());
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "写完毕------" + System.currentTimeMillis());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.writeLock().unlock();
}
}
}
输出结果:
写锁测试+++++++++
Thread-0正在读......1696479655992
Thread-0读退出------1696479659000
Thread-1正在写++++++1696479659000
Thread-1写完毕------1696479662008
读锁测试--------
Thread-2正在读......1696479665999
Thread-3正在读......1696479665999
Thread-2读退出------1696479669003
Thread-3读退出------1696479669003
Process finished with exit code 0