Java并发Lock接口实现示例详解

 更新时间:2023年06月22日 09:07:23   作者:右耳菌  
这篇文章主要为大家介绍了Java并发Lock接口,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

Locks包 类层次结构

Lock接口

方法签名描述说明
void lock();获取锁(不死不休)一直获取锁,直到拿到为止
boolean tryLock();获取锁(浅尝辄止)尝试获得锁,获取不到就算了
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;获取锁(过时不候)超时限制,超过时间就放弃
void lockInterruptibly() throws InterruptedException;获取锁(任人摆布)可以在外部通过方法中断
void unlock();释放锁
Condition newCondition();

结论:

1、lock()最常用;

2、lockInterruptibly()方法一般更昂贵,有的impl可能没有实现lockInterruptibly(),只有真的需要效应中断时,才使用,使用之前看看impl对该方法的描述。

trylock

package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class GetLock_Demo {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        lock.lock(); //主线程拿到锁
        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean rs = lock.tryLock();
                System.out.println("是否获取到锁: " + rs);
            }
        });
        th.start();
        Thread.sleep(2000L);
        th.interrupt();//中断线程运行
        System.out.println("th 线程被中断了");
    }
}

是否获取到锁: false
th 线程被中断了

  • trylock带超时
package lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class GetLock_Demo {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        lock.lock(); //主线程拿到锁
        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean rs = false;
                try {
                    rs = lock.tryLock(1, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("是否获取到锁: " + rs);
            }
        });
        th.start();
        Thread.sleep(2000L);
        th.interrupt();//中断线程运行
        System.out.println("th 线程被中断了");
    }
}

是否获取到锁: false
th 线程被中断了

lockInterruptibly

package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class GetLock_Demo {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        lock.lock(); //主线程拿到锁
        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lockInterruptibly();
                } catch (InterruptedException e) {
                    System.out.println("获取锁时被中断了");
                    e.printStackTrace();
                }
            }
        });
        th.start();
        Thread.sleep(2000L);
        th.interrupt();//中断线程运行
        System.out.println("th 线程被中断了");
    }
}

 th 线程被中断了
获取锁时被中断了
java.lang.InterruptedException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
    at lock.GetLock_Demo$1.run(GetLock_Demo.java:16)
    at java.lang.Thread.run(Thread.java:748)

lock and unlock

package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class GetLock_Demo {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        lock.lock(); //主线程拿到锁
        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("尝试获得锁");
                lock.lock();
                System.out.println("获得锁了");
            }
        });
        th.start();
        Thread.sleep(2000L);
        th.interrupt();//中断线程运行
        System.out.println("th 线程被中断了");
        Thread.sleep(5000L);
        lock.unlock();
    }
}

尝试获得锁
th 线程被中断了
获得锁了

Condition

Condition 一般是将其中的await和signal成对使用的,且一般是await在前signal在后,而且调用的使用,应该确保本身是获取到锁的情况下,不然会出现以下问题:

1. await 和 signal 方法应该在lock内部调用,否则会发生 IllegalMonitorStateException 异常

package lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Condition_Demo {
    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                lock.lock();
                try {
                    System.out.println("当前线程:" + Thread.currentThread().getName() + "获得锁");
                    condition.await(); //因为这里将线程挂起,所以后面无法执行
                    System.out.println("当前线程:" + Thread.currentThread().getName() + "开始执行~");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };
        thread.start();
        Thread.sleep(2000L);
        System.out.println("休眠2s,来控制线程");
        condition.signal(); //直接唤醒会报错,因为lock方法执行在Thread-0线程内部,而我们代码在这里执行的是main线程,所以会报错,
    }
}

 当前线程:Thread-0获得锁
休眠2s,来控制线程
Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
    at lock.Condition_Demo.main(Condition_Demo.java:33)

2. signal应该在await后调用,否则会导致死锁

package lock;
import sync.ReentrantLockDemo;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Condition_Demo {
    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000L);
                    System.out.println("休眠3秒,等待主线程先执行.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.lock();
                try {
                    System.out.println("当前线程:" + Thread.currentThread().getName() + "获得锁");
                    condition.await(); //因为这里将线程挂起,所以后面无法执行
                    System.out.println("当前线程:" + Thread.currentThread().getName() + "开始执行~");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };
        thread.start();
        Thread.sleep(2000L);
        System.out.println("休眠2s,来控制线程");
        lock.lock();
        condition.signal(); //直接唤醒会报错,因为lock方法执行在Thread-0线程内部,而我们代码在这里执行的是main线程,所以会报错,
        lock.unlock(); //获取到了这把锁,然后解锁.
        //2.当然这里会出现死锁的,如果signal方法在我们的await之前执行,那么这里就会死锁
    }
}

休眠2s,来控制线程
休眠3秒,等待主线程先执行.
当前线程:Thread-0获得锁
// 这里死锁了

  • 使用condition实现阻塞队列的例子
package lock;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BlockingQueue_Demo {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue kaneBlockingQueue = new BlockingQueue(6);
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    kaneBlockingQueue.put("x" + i);
                }
            }
        }.start();
        Thread.sleep(1000L);
        System.out.println("开始取元素");
        for (int i = 0; i < 8; i++) {
            kaneBlockingQueue.take();
            Thread.sleep(2000);
        }
    }
}
class BlockingQueue {
    List<Object> list = new ArrayList<>();
    private Lock lock = new ReentrantLock();
    private Condition putCondition = lock.newCondition(); //condition可以有多个,针对不同的操作放入不同condition,相当于等待队列
    private Condition takeCondition = lock.newCondition();
    private int length;
    public BlockingQueue(int length) {
        this.length = length;
    }
    public void put(Object obj) {
        lock.lock(); //思考一个读一个写,为什么要加锁呢?
        try {
            while (true) {
                if (list.size() < length) { //我们集合的长度不能超过规定的长度,才能向里面放东西
                    list.add(obj);
                    System.out.println("队列中放入元素:" + obj);
                    takeCondition.signal();
                    return;
                } else { //如果放不进去,就该阻塞. --利用condition实现
                    putCondition.await();//挂起
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public Object take() {
        lock.lock();
        try {
            while (true) {
                if (list.size() > 0) {
                    Object obj = list.remove(0);
                    System.out.println("队列中取得元素:" + obj);
                    putCondition.signal();
                    return obj;
                } else {
                    takeCondition.await();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            return null;
        }
    }
}

队列中放入元素:x0
队列中放入元素:x1
队列中放入元素:x2
队列中放入元素:x3
队列中放入元素:x4
队列中放入元素:x5
开始取元素
队列中取得元素:x0
队列中放入元素:x6
队列中取得元素:x1
队列中放入元素:x7
队列中取得元素:x2
队列中放入元素:x8
队列中取得元素:x3
队列中放入元素:x9
队列中取得元素:x4
队列中取得元素:x5
队列中取得元素:x6
队列中取得元素:x7

Process finished with exit code 0

可重入锁 ReentrantLock

一般来说,如果可重入锁的加锁次数是n,那么解锁次数也得是n才能完全释放锁,否则,如果小于n 则无法正常释放锁,此时如果有别的线程要加锁,则无法获取到锁而被阻塞;如果大于n,则会触发 IllegalMonitorStateException 异常, ReentrantLock 默认是使用非公平锁,如果要使用公平锁,可以使用 new ReentrantLock(true) 来创建。

  • 小于n的情况
package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Reentrant_Demo {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "获得第1次锁");
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "获得第2次锁");
        lock.unlock();
        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "开始去释放锁");
                lock.lock();
                System.out.println("获得锁成功~~~");
                lock.unlock();
            }
        }.start();
    }
}
main获得第1次锁
main获得第2次锁
Thread-0开始去释放锁
// 子线程获取锁失败导致阻塞了
  • 大于n的情况修改成3次unlock
        // 修改成3次unlock
        lock.unlock();
        lock.unlock();
        lock.unlock();
main获得第1次锁
main获得第2次锁
Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
    at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
    at lock.Reentrant_Demo.main(Reentrant_Demo.java:18)

简单说明图

实现一个ReenrantLock的demo版本 - 一个现实思想的简单版本

package lock;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
public class ReentrantLock_Demo implements Lock {
    //记录锁的拥有者
    AtomicReference<Thread> owner = new AtomicReference<>();
    //记录重入次数的count
    AtomicInteger count = new AtomicInteger(0);
    //等待队列
    private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue();
    @Override
    public boolean tryLock() {
        //判断count值是否为0,如果count不等于0,说明锁被占用
        int ct = count.get();
        //判断锁是不是自己占用的,做重入
        if (ct != 0) {
            if (Thread.currentThread() == owner.get()) {
                count.set(ct + 1);
                return true;
            }
        } else { //若count为0 ,表示当前锁未被占用,通过CAS操作
            if (count.compareAndSet(ct, ct + 1)) {
                owner.set(Thread.currentThread());          //如果不是自己,进入队列
                return true;
            }
        }
        return false;
    }
    @Override
    public void lock() {
        if (!tryLock()) {
            //加入等待队列
            waiters.offer(Thread.currentThread());
            while (true) {
                //若线程是队列头部,先判断一次,现在能不能去抢,然后再去加锁
                Thread head = waiters.peek();
                if (head == Thread.currentThread()) {
                    if (!tryLock()) {
                        LockSupport.park();
                    } else {
                        waiters.poll();
                        return;
                    }
                } else {
                    LockSupport.park();
                }
            }
        }
    }
    public boolean tryUnlock() {
        if (owner.get() != Thread.currentThread()) {
            throw new IllegalMonitorStateException();
        } else {
            int ct = count.get();
            int nextc = ct - 1;
            count.set(nextc);
            if (nextc == 0) { //可重入锁被加锁多次,一旦为0 就释放锁,如果不是0,还得继续释放
                owner.compareAndSet(Thread.currentThread(), null);
                return true;
            } else {
                return false;
            }
        }
    }
    @Override
    public void unlock() {
        if (tryUnlock()) {
            Thread head = waiters.peek();
            if (head != null) {
                LockSupport.unpark(head);
            }
        }
    }
    /**
     * 暂时忽略
     *
     * @throws InterruptedException
     */
    @Override
    public void lockInterruptibly() throws InterruptedException {
    }
    /**
     * 暂时忽略
     *
     * @throws InterruptedException
     */
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }
    /**
     * 暂时忽略
     *
     * @throws InterruptedException
     */
    @Override
    public Condition newCondition() {
        return null;
    }
}

以上就是Java并发Lock接口实现示例详解的详细内容,更多关于Java并发Lock接口的资料请关注脚本之家其它相关文章!

相关文章

  • Java中RocketMQ的延迟消息详解

    Java中RocketMQ的延迟消息详解

    这篇文章主要介绍了Java中RocketMQ的延迟消息详解,RocketMQ是一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠、万亿级容量、灵活可伸缩的消息发布与订阅服务,需要的朋友可以参考下
    2023-09-09
  • JavaWeb项目实现文件上传动态显示进度实例

    JavaWeb项目实现文件上传动态显示进度实例

    本篇文章主要介绍了JavaWeb项目实现文件上传动态显示进度实例,具有一定的参考价值,有兴趣的可以了解一下。
    2017-04-04
  • 聊聊springboot 整合 hbase的问题

    聊聊springboot 整合 hbase的问题

    这篇文章主要介绍了springboot 整合 hbase的问题,文中给大家提到配置linux服务器hosts及配置window hosts的相关知识,需要的朋友可以参考下
    2021-11-11
  • Java 正则表达式功能及应用

    Java 正则表达式功能及应用

    自从jdk1.4推出java.util.regex包,就为我们提供了很好的Java正则表达式应用平台,因为Java正则表达式是一个很庞杂的体系。
    2010-03-03
  • 使用 Java 开发 Gradle 插件的步骤

    使用 Java 开发 Gradle 插件的步骤

    这篇文章主要介绍了使用 Java 开发 Gradle 插件的步骤,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • Java concurrency之Condition条件_动力节点Java学院整理

    Java concurrency之Condition条件_动力节点Java学院整理

    Condition的作用是对锁进行更精确的控制。下面通过本文给大家分享Java concurrency之Condition条件的相关知识,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2017-06-06
  • Java 并发编程中如何创建线程

    Java 并发编程中如何创建线程

    这篇文章主要介绍了Java 并发编程中如何创建线程,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • 解决springboot集成rocketmq关于tag的坑

    解决springboot集成rocketmq关于tag的坑

    这篇文章主要介绍了解决springboot集成rocketmq关于tag的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • java图形界面AWT编写计算器

    java图形界面AWT编写计算器

    这篇文章主要为大家详细介绍了基于java语言下图形界面AWT编写计算器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • SpringCloud声明式Feign客户端调用工具使用

    SpringCloud声明式Feign客户端调用工具使用

    这篇文章主要为大家介绍了SpringCloud声明式Feign客户端调用工具使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08

最新评论