并发编程(4)-显示锁

Java是靠synchronized关键字实现锁功能的。synchronized关键字将会隐式地获取锁,但是它将锁的获取和释放固化了。也就是先获取再释放。synchronized和Lock比较资源消耗会少,因为它是Java语言特性,而Lock需要new对象来开销内存。所以平常使用synchronized即可。

Lock特性

特性描述
尝试非阻塞地获取 锁当前线程尝试获取 锁,如果这一时刻锁没有被其它线程获取 ,则成功获取并持有锁
能被中断地获取锁与synchronized不同,获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放
超时获取 锁在指定的截止时间之前获取 锁,如果截止时间到了仍旧无法获取 锁,则返回

Lock的标准用法

public class LockTest {
    private Lock lock = new ReentrantLock();
    
    public void test(){
        lock.lock();
        try {
            System.out.println("...");
        } finally {
            lock.unlock();
        }
    }
}

Lock常用API

方法名称描述
void lock()获取锁
void lockInterruptibly() throws InterruptedException可中断地获取锁,和lock()方法不同之处在于该方法会响应中断,即在锁的获取中可以中断当前线程
boolean tryLock()尝试非阻塞的获取锁,调用该方法后立刻返回,如果能够获取则返回true,否则返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException

超时的获取锁,当前线程在以下3种情况下会返回:

1、当前线程在超时时间内获得了锁

2、当前线程在超时时间内被中断

3、超时时间结束,返回false

void unlock()释放锁

ReentrantLock_可重入锁

锁的可重入

    同一个线程对于已经获得到的锁,可以多次继续申请到该锁的使用权。synchronized关键字隐匿的支持重入性。同样的ReentrantLock在调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。

公平和非公平

    ReentrantLock提供了一个构造函数,能够控制锁是否是公平的。

    new ReentrantLock(true); 公平锁,默认为非公平锁

公平锁机制效率往往低于非公平锁

    在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟。假如A线程持有一个锁的同时B线程来请求这个锁,那么B线程将被挂起。当A线程释放锁的同时C线程来请求锁,这时C可能会在B被唤醒之前得到锁并执行完自己的任务后再释放锁,但如果是公平锁的话,C线程将要等待B线程获取锁并执行完自己的任务并释放之后才能获得锁。

ReentrantReadWriteLock_读写锁

特点

    读共享、写独占、读写互斥。

    ReentrantLock为排他锁,即在同一时刻只允许一个线程进行访问。而ReentrantReadWriteLock读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁:一个读锁、一个写锁。这样它的并发性相比其它排他锁有了很大提高,因为现实场景一般是读多写少[比例大概为:10/1],而读写锁正好有读共享的特点。

性能对比测试示例

public class SynRwCompareTest {
    // 读写线程数
    private static final Integer R_COUNT = 100, W_COUNT = 10;
    // 读写锁计时器
    private static CyclicBarrier rwCyclicBarrier = new CyclicBarrier(W_COUNT + (R_COUNT * W_COUNT) + 1);
    // 排他锁计时器
    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(W_COUNT + (R_COUNT * W_COUNT) + 1);

    private final ReadWriteLock rw = new ReentrantReadWriteLock();
    private final Lock rLock = rw.readLock();//读锁
    private final Lock wLock = rw.writeLock();//写锁

    // 读写锁:读方法
    public void getRWMethod(){
        rLock.lock();
        try {
            //System.out.println("执行读写锁读方法------");
            SleepTools.ms(2);
        } finally {
            rLock.unlock();
        }
    }

    // 读写锁:写方法
    public void setRWMethod(){
        wLock.lock();
        try {
            //System.out.println("执行读写锁写方法------");
            SleepTools.ms(2);
        } finally {
            wLock.unlock();
        }
    }

    // 读写锁:读线程
    private static class GetRWThreadClass implements Runnable {
        SynRwCompareTest compareTest = null;

        public GetRWThreadClass(SynRwCompareTest compareTest) {
            this.compareTest = compareTest;
        }

        @Override
        public void run() {
            compareTest.getRWMethod();
            try {
                rwCyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    // 读写锁:写线程
    private static class SetRWThreadClass implements Runnable {
        SynRwCompareTest compareTest = null;

        public SetRWThreadClass(SynRwCompareTest compareTest) {
            this.compareTest = compareTest;
        }

        @Override
        public void run() {
            compareTest.setRWMethod();
            try {
                rwCyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    // 排他锁:读方法
    public synchronized void getMethod(){
        //System.out.println("执行排他锁读方法------");
        SleepTools.ms(2);
    }

    // 排他锁:写方法
    public synchronized void setMethod() {
        //System.out.println("执行排他锁写方法------");
        SleepTools.ms(2);
    }

    // 排他锁:读线程
    private static class GetThreadClass implements Runnable {
        SynRwCompareTest compareTest = null;

        public GetThreadClass(SynRwCompareTest compareTest) {
            this.compareTest = compareTest;
        }

        @Override
        public void run() {
            compareTest.getMethod();
            try {
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    // 排他锁:写线程
    private static class SetThreadClass implements Runnable {
        SynRwCompareTest compareTest = null;

        public SetThreadClass(SynRwCompareTest compareTest) {
            this.compareTest = compareTest;
        }

        @Override
        public void run() {
            compareTest.setMethod();
            try {
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

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

        SynRwCompareTest compareTest = new SynRwCompareTest();

        long start = System.currentTimeMillis();
        for (int i = 0; i < W_COUNT; i++) {
            Thread wThread = new Thread(new SetThreadClass(compareTest));
            for (int i1 = 0; i1 < R_COUNT; i1++) {
                new Thread(new GetThreadClass(compareTest)).start();
            }
            wThread.start();
        }
        cyclicBarrier.await();
        System.out.println("~~~~~~~~~~排他锁执行总耗时:" + (System.currentTimeMillis() - start) + "毫秒~~~~~~~~~~");

        long rwStart = System.currentTimeMillis();
        for (int i = 0; i < W_COUNT; i++) {
            Thread wThread = new Thread(new SetRWThreadClass(compareTest));
            for (int i1 = 0; i1 < R_COUNT; i1++) {
                new Thread(new GetRWThreadClass(compareTest)).start();
            }
            wThread.start();
        }
        rwCyclicBarrier.await();
        System.out.println("~~~~~~~~~~读写锁执行总耗时:" + (System.currentTimeMillis() - rwStart) + "毫秒~~~~~~~~~~");

    }

}

/*
~~~~~~~~~~排他锁执行总耗时:2860毫秒~~~~~~~~~~
~~~~~~~~~~读写锁执行总耗时:198毫秒~~~~~~~~~~
*/

Condition接口

    Object类有wait()、wait(long timeout)、nofity()、nofityAll()方法与synchronized关键字配合实现线程的等待/通知模式。

    Condition接口也提供了类似Object类的监视器方法,与Lock配合可实现等待/通知模式。

Condition接口常用方法

方法名称说明
void await() throws InterruptedException

使当前线程进入等待状态直到被通知(signal)或中断

线程被通知的情况:

1、其他线程调用该Condition接口的signal()或signalAll()方法

2、其他线程调用interrupt()方法中断当前线程

3、如果当前线程从await()方法返回,那么表明此线程已经获取了Condition对象对应的锁

void awaitUninterruptibly()当前线程进入等待状态直到被通知,此方法对中断不敏感
long awaitNanos(long nanosTimeout) throws InterruptedException当前线程进入等待状态直到被通知、中断或超时。返回值表示剩余的时间,如果在nanosTimeout纳秒之前被唤醒,那么返回值就是(nanosTimeout-实际耗时)。如果返回值是0或者负数,那么表示已经超时。
boolean awaitUnitl(Date deadline) throws InterruptedException当前线程进入等待状态直到被通知、中断或者到某个时间。如果没有到指定时间就被通知,方法返回true,否则表示到了指定时间,方法返回false
void signal()唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关的锁。推荐
void signalAll()唤醒所有等待在Condition上的线程,能够从等待方法返回的线程必须获得与Condition相关联的锁

示例

public class ConditionTest {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void test(){
        try {
            lock.lock();
            System.out.println("abc");
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

LockSupport

    LockSupport 定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而LockSupport 也成为构建同步组件的基础工具。
    LockSupport 定义了一组以park 开头的方法用来阻塞当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程。LockSupport 增加了park(Object blocker)、parkNanos(Object blocker,long nanos)和parkUntil(Object blocker,long deadline)3 个方法,用于实现阻塞当前线程的功能,其中参数blocker是用来标识当前线程在等待的对象(以下称为阻塞对象),该对象主要用于问题排查和系统监控。

_____个人笔记_____((≡^⚲͜^≡))_____欢迎指正_____

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值