ReentrantLock与Synchronized 使用

ReentrantLock使用

1.获取锁

ReentrantLock包含两个try Lock()方法:

tryLock()

tryLock(long timeout, TimeUnit unit)timeout为最长等待时间,TimeUnit为时间单位。

  • 使用 lock.tryLock() 方法会立刻返回获取锁是否成功。如果成功则返回true,反之则返回false。
  • tryLock(long timeout, TimeUnit unit)方法可以设置指定等待时间,在尝试一定时间后若成功则返回true,反之则返回false。期间一直在阻塞队列中。

2.中断锁

ReentrantLock中提供lockInterruptibly()方法可以通过调用阻塞线程的t1.interrupt();方法打断。

若在线程中调用 reentrantLock.lockInterruptibly();方法,则表示在进入阻塞队列中可以被其他线程打断。

若不调用则不可被打断;

public class ReentrantTest{

    private static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
           /* try {
                // 如果没有竞争那么此方法就会获取 lock 对象锁
                // 如果有竞争就进入阻塞队列,可以被其它线程用 interruput 方法打断
                log.debug("尝试获得锁");
                reentrantLock.lockInterruptibly();  //表明可中断锁
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("t1线程没有获得锁,被打断...return");
                return;
            }*/ //取消这段注释则可测试,中断锁

            try {
                log.debug("t1线程获得了锁");
            } finally {
                reentrantLock.unlock();
            }
        }, "t1");

        // t1启动前 主线程先获得了锁
        reentrantLock.lock();
        t1.start();
        Thread.sleep(500);
        log.debug("打断t1");
        t1.interrupt();
    }

}

3.公平锁

ReentrantLock默认是非公平锁, 可以指定为公平锁new ReentrantLock(true)。

在线程获取锁失败,进入阻塞队列时,先进入的会在锁被释放后先获得锁。这样的获取方式就是公平的。

公平锁

  • 优点:所有的线程都能得到资源,不会在队列中一直获取不到。
  • 缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

非公平锁

  • 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
  • 部分线程会在队列中一直获取不到锁。

4. Condition

ReentrantLock是使用Condition对象来实现waitnotify的功能。

使用Condition时,引用的Condition对象必须从Lock实例的newCondition()返回,这样才能获得一个绑定了Lock实例的Condition实例。

private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();

每一个锁都有自己的Condition,通过调用各自的await()signal()signalAll()实现不同线程的切换

  • await()会释放当前锁,进入等待状态;
  • signal()会唤醒某个等待线程;
  • signalAll()会唤醒所有等待线程;
  • 唤醒线程从await()返回后需要重新获得锁。
public class AlternatePrintTest2 {

    public static void main(String[] args) throws InterruptedException {

        AwaitSignal awaitSignal = new AwaitSignal(5);

        Condition a_condition = awaitSignal.newCondition();
        Condition b_condition = awaitSignal.newCondition();
        Condition c_condition = awaitSignal.newCondition();

        new Thread(() -> {
            awaitSignal.print("a", a_condition, b_condition);
        }, "a").start();

        new Thread(() -> {
            awaitSignal.print("b", b_condition, c_condition);
        }, "b").start();

        new Thread(() -> {
            awaitSignal.print("c", c_condition, a_condition);
        }, "c").start();

        Thread.sleep(1000);
        System.out.println("开始...");
        awaitSignal.lock();
        try {
            a_condition.signal();  // 首先唤醒a线程
        } finally {
            awaitSignal.unlock();
        }
    }

}

class AwaitSignal extends ReentrantLock {
    private final int loopNumber;

    public AwaitSignal(int loopNumber) {
        this.loopNumber = loopNumber;
    }

    public void print(String str, Condition current, Condition next) {
        for (int i = 0; i < loopNumber; i++) {
            lock();
            try {
                try {
                    current.await();
                    System.out.print(str);
                    // 唤醒下一个
                    next.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } finally {
                unlock();
            }
        }
    }
}

synchronized的使用

synchronized是Java中的关键字,其作用的是对象

修饰对象有四种方式:

  1. 代码块:被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

    synchronized (obj) {
        // 需要同步的代码块
    }
    
  2. 方法:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

    public synchronized void methodName() {
        // synchronized 代码块
    }
    
  3. 静态的方法:被修饰的方法称为同步静态方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

    public static synchronized void staticMethodName() {
        // synchronized 代码块
    }
    
  4. 类:其作用范围是 synchronized 后面括号括起来的部分,作用对象是这个类的所有对象。

    class ClassName {
    	public void method() {
    		synchronized(ClassName.class) {
    			// 同步代码块
    		}
    	}
    }
    
Synchronized ReentrantLock
类型 隐式锁,JUC库提供 显示锁,Java API提供
公平锁 非公平锁 默认非公平锁,可通过构造函数**new ReentrantLock(true)**指定公平锁
中断锁 无法中断 ReentrantLock中提供lockInterruptibly()方法可以通过调用阻塞线程的t1.interrupt();方法打断。
获取锁 必须等待锁被释放 ReentrantLock提供两个**try Lock()**方法,尝试获取锁
唤醒 采用Object wait()和notify()/ notifyAll()实现唤醒 从Lock实例的**newCondition()**返回Condition对象
锁状态与监控 无法查询锁状态 提供**isLock()**判断是否占用,**getHoldCount()**获取当前重入次数

你可能感兴趣的:(java)