java线程同步之ReentrantLock

ReentrantLock初识:

ReentrantLock与synchronized关键字一样都是用于实现线程之间的同步操作,两者效果基本一直。JDK1.5引入ReentrantLock,因为它相比于synchronized来说显得更加灵活,扩张功能更加强大,例如嗅探锁定,多路分支通知等功能。

public class MyService {

    private ReentrantLock reentrantLock = new ReentrantLock();

    public void methodA() {
        try {
            reentrantLock.lock();
            System.out.println("methodA begin lock" + "Thread Name is ---->" + Thread.currentThread().getName() + "  time is " +  System.currentTimeMillis());
            System.out.println();
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
        System.out.println("methodA finish  " + "Thread Name is ---->" + Thread.currentThread().getName() + "  time is " + 
        System.currentTimeMillis());
    }

    public void methodB() {
        try {
            reentrantLock.lock();
            System.out.println("methodB begin lock" + "Thread Name is ---->" + Thread.currentThread().getName() + "  time is " + System.currentTimeMillis());
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
        System.out.println("methodB finish  " + "Thread Name is ---->" + Thread.currentThread().getName() + "  time is " + System.currentTimeMillis());
    }
}
public class ThreadA extends Thread{

    private MyService service;

    public ThreadA(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        service.methodA();
    }
}
public class ThreadB extends Thread{

    private MyService service;

    public ThreadB(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        service.methodB();
    }
}
public class Main {

    public static void main(String[] args) {
        MyService service = new MyService();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("ThreadA");
        ThreadB threadB = new ThreadB(service);
        threadB.setName("ThreadB");
        threadA.start();
        threadB.start();
    }

}

这里写图片描述
上面例子可以看出,ReentrantLock锁定的是对象,多个线程访问同一对象的同一个Lock对象的代码时会出现互斥。ReentrantLock在使用时lock()与unlock()方法必须成对出现,否则会出现死锁

ReentrantLock特性:

Condition使用方式:

synchronized关键字实现同步时,可以通过Object中的wait(),notify()方法实现等待/通知,但是notify调用时只是会随机唤醒其中一个等待线程,无法唤醒特定线程,如果使用notifyAll方法会将所有等待线程都唤醒。而使用ReentrantLock在这方面具有优势,Lock可以根据不同的Condition唤醒指定线程,实现多路等待。

public class MyService {

    private ReentrantLock reentrantLock = new ReentrantLock();
    private Condition conditionA = reentrantLock.newCondition();
    private Condition conditionB = reentrantLock.newCondition();

    public void awaitA() {
        try{
            reentrantLock.lock();
            System.out.println("begin awaitA time is  " + System.currentTimeMillis() + " and Thread is  " + Thread.currentThread().getName());
            conditionA.await();
            System.out.println("end awaitA time is  " + System.currentTimeMillis() + " and Thread is  " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
    }

    public void awaitB() {
        try{
            reentrantLock.lock();
            System.out.println("begin awaitB time is  " + System.currentTimeMillis() + " and Thread is  " + Thread.currentThread().getName());
            conditionA.await();
            System.out.println("end awaitB time is  " + System.currentTimeMillis() + " and Thread is  " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
    }

    public void signalA() {
        try {
            reentrantLock.lock();
            System.out.println("begin signalA time is  " + System.currentTimeMillis() + " and Thread is  " + Thread.currentThread().getName());
            conditionA.signalAll();
        } finally {
            reentrantLock.unlock();
        }
    }

    public void signalB() {
        try {
            reentrantLock.lock();
            System.out.println("begin signalB time is  " + System.currentTimeMillis() + " and Thread is  " + Thread.currentThread().getName());
            conditionA.signalAll();
        } finally {
            reentrantLock.unlock();
        }
    }
....
}
public class Main {

    public static void main(String[] args) {
        MyService service = new MyService();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("ThreadA");
        ThreadB threadB = new ThreadB(service);
        threadB.setName("ThreadB");
        threadA.start();
        threadB.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        service.signalA();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        service.signalB();
    }

}

这里写图片描述
ThreadA、ThreadB中run()分别调用service的awaitA、awaitB方法,为了验证使用不同的condition进行await操作时,需要使用对应的condition进行唤醒操作。我们使用了signalAll()方法,该方法与notifyAll方法一样,但是它只是唤醒使用同一个condition的等待线程,对于不同的condition不起作用,这就是ReentrantLock的多路等待实现方式。

tryLock获取锁:
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

tryLock()方法会立即返回结果,如果可以获得锁则返回true,反之返回false。带参数的tryLock很容易理解,就是在获取锁失败时,会等待一段时间(传入的参数),当等待时间达到传入值时,返回获取锁的结果,同tryLock方法一样。

公平性与非公平性锁:
public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

ReentrantLock默认实现为非公平锁。在公平的锁上,线程按照他们发出请求的顺序获取锁,但在非公平锁上,则允许‘插队’:当一个线程请求非公平锁时,如果在发出请求的同时该锁变成可用状态,那么这个线程会跳过队列中所有的等待线程而获得锁。非公平的ReentrantLock 并不提倡插队行为,但是无法防止某个线程在合适的时候进行插队。非公平锁与公平锁相比,性能上会更优。

在公平的锁中,如果有另一个线程持有锁或者有其他线程在等待队列中等待这个所,那么新发出的请求的线程将被放入到队列中。而非公平锁上,只有当锁被某个线程持有时,新发出请求的线程才会被放入队列中。

结束语

本篇仅简单介绍ReentrantLock的使用方式和一些基础知识,关于原理实现会在后面介绍了AbstractQueuedSynchronizer之后再进行分享。

猜你喜欢

转载自blog.csdn.net/hb_csu/article/details/80479999
今日推荐