首页 > 其他分享 >并发学习记录07:ReentrantLock

并发学习记录07:ReentrantLock

时间:2022-08-22 12:34:24浏览次数:53  
标签:07 lock ReentrantLock Chopstick 并发 debug new public

特点

相比于synchronized,ReentrantLock具有可中断,可以设置超时时间,可以设置为公平锁,支持多个条件变量的特点,它和synchronized一样,都支持可重入

基本语法

// 获取锁
reentrantLock.lock();
try {
 // 临界区
} finally {
 // 释放锁
 reentrantLock.unlock();
}

可重入

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁,如果是不可重入锁,那么第二次获取锁时,自己也会被挡住

可重入验证代码

@Slf4j(topic = "ch.ReentrantLockTest01")
public class ReentrantLockTest01 {
    static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        method1();
    }

    public static void method1() {
        lock.lock();
        try {
            log.debug("method1");
            //验证可重入
            method2();
        } finally {
            lock.unlock();
        }
    }

    public static void method2() {
        lock.lock();
        try {
            log.debug("method2");
            method3();
        } finally {
            lock.unlock();
        }
    }

    public static void method3() {
        lock.lock();
        try {
            log.debug("method3");
        } finally {
            lock.unlock();
        }
    }
}

可打断

可中断锁是指抢占过程可以被中断的锁,如下:

@Slf4j(topic = "ch.ReentrantLockTest02")
public class ReentrantLockTest02 {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("启动");
            try {
                //可打断的上锁
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("被打断");
                return;
            }
            log.debug("上锁成功");
            lock.unlock();
        }, "t1");

        lock.lock();
        log.debug("获得了锁");
        t1.start();
        Thread.sleep(1);
        log.debug("执行打断");
        t1.interrupt();
        lock.unlock();
    }
}

锁超时

其实这里用了tryLock方法实现了,tryLock有两种使用方法,一种是直接tryLock,返回值是Boolean对象,表示的是用来尝试获取锁,如果成功就返回true,如果失败就返回false,这个方法无论成功失败都会立刻返回。tryLock还有一个重载方法public boolean tryLock(long timeout, TimeUnit unit),表示在这个时间范围内成功获取了锁返回true,没获得就返回false。实例如下:

//测试锁超时
@Slf4j(topic = "ch.ReentrantLockTest03")
public class ReentrantLockTest03 {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("启动");
            if (!lock.tryLock()) {
                log.debug("获取锁失败,返回");
                return;
            }
            try {
                log.debug("获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        log.debug("主线程获取了锁");
        lock.lock();
        t1.start();
        try {
            Thread.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

使用tryLock解决哲学家就餐问题

//使用tryLock解决哲学家就餐问题
@Slf4j(topic = "ch.ReentrantLockTest04")
public class ReentrantLockTest04 {
    public static void main(String[] args) {
        Chopstick c1 = new Chopstick("1", true);
        Chopstick c2 = new Chopstick("2", true);
        Chopstick c3 = new Chopstick("3", true);
        Chopstick c4 = new Chopstick("4", true);
        Chopstick c5 = new Chopstick("5", true);
        new Philosopher("哲学家1", c1, c2).start();
        new Philosopher("哲学家2", c2, c3).start();
        new Philosopher("哲学家3", c3, c4).start();
        new Philosopher("哲学家4", c4, c5).start();
        new Philosopher("哲学家5", c5, c1).start();
    }
}

@Slf4j(topic = "ch.Chopstick")
class Chopstick extends ReentrantLock {
    String name;

    public Chopstick(String name, boolean fair) {
        super(fair);
        this.name = name;
    }

    @Override
    public String toString() {
        return "Chopstick{" +
                "name='" + name + '\'' +
                '}';
    }
}

@Slf4j(topic = "ch.Philosopher")
class Philosopher extends Thread {
    Chopstick left;
    Chopstick right;

    public Philosopher(String name, Chopstick left, Chopstick right) {
        super(name);
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while (true) {
            if (left.tryLock()) {
                try {
                    if (right.tryLock()) {
                        try {
                            eat();
                        } finally {
                            right.unlock();
                        }
                    }
                } finally {
                    left.unlock();
                }
            }
        }
    }

    private void eat() {
        log.debug("eating....");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

公平锁

ReentrantLock 默认是不公平的,公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象。公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。因此,ReentrantLock默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量。

ReentrantLock的两个构造方法:

// 默认非公平
public ReentrantLock() {
  sync = new NonfairSync();
}

// 根据传参来实现公平或非公平锁
public ReentrantLock(boolean fair) {
	sync = fair ? new FairSync() : new NonfairSync();
}

条件变量

synchronized中也有条件变量,起的作用类似于synchronized中的那个waitSet休息室,当条件不满足时,进入waitSet等待

ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比
*synchronized的那些不满足条件的线程都在一间休息室等消息
*而 ReentrantLock 支持多间休息室,可以按照不同的资源等待条件new不同的“休息室”,唤醒时也是按照不同的条件来唤醒的

使用要点:
await前需要获得锁
await执行后,会释放锁,进入conditionObject等待
await线程被唤醒(或打断,或超时)后需重新竞争lock锁
竞争lock锁成功后,从await后继续执行

标签:07,lock,ReentrantLock,Chopstick,并发,debug,new,public
From: https://www.cnblogs.com/wbstudy/p/16609668.html

相关文章

  • 并发编程学习
    SemaphoreSemaphore可以允许多个线程访问一个临界区。应用:实现线程池CountDownLatch应用:业务原始状态:一个线程执行查询订单,查询派送单,对比差异,写入数据库优化后:......
  • 使用线程池,并发计算1~50、51~100的和,再进⾏汇总统计。
    知识点:获取线程池、提交任务、获取返回值 获取线程池的几种方式:newFixedThreadPool(intnThreads)获取固定数量的线程池。参数:指定线程池中线程的数量。(使用这种)newC......
  • 【Java面试】并发编程高频面试题,请你说一下你对Happens-Before的理解
    “请你说一下你对Happens-Before的理解”你听到这个问题的时候,知道怎么回答吗?大家好,我是Mic,一个工作了14年的Java程序员。并发编程是面试过程中重点考察的方向,能够考察......
  • 性能测试-压测工具ab-1024个并发以下可用以及ab和wrk的优缺点
    ab全称:ApacheBench,用于web性能压力测试,ab命令会创建很多的并发访问线程,模拟多个访问者同时对某一URL地址进行访问。ab命令对发出负载的计算机要求很低,不会占用很高CPU......
  • go基础系列~并发协程
    零基础协程一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程,协程的切换和创建完全是用户决定的goroutine相对于线程:1.Gorouti......
  • GP中的并发控制
    Greenplum数据库使用了PostgreSQL的多版本并发控制(MVCC)模型来管理对于堆表的并发事务。 铜锅MVCC,每一个查询都在它开始时的一个数据库快照上操作。在执行时,一个查询不能......
  • 107.binary-tree-level-order-traversal-ii 二叉树的层序遍历II
    参考102.binary-tree-level-order-traversal二叉树的层序遍历,翻转一下结果数组就好了。classSolution{public:vector<vector<int>>levelOrderBottom(TreeNode......
  • 并发并行
    并发并行并发在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运......
  • ReentrantLock
    简介ReentrantLock重入锁,是实现Lock接口的一个类。支持重入性(表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞,synchronized隐式支持重入性)Reen......
  • 并发编程的艺术
    现在我们介绍避免死锁的几个常见方法。❑避免一个线程同时获取多个锁。❑避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。❑尝试使用定时锁,使......