并发编程12-显示锁

内部所拥有比较好的性能,但是在灵活性方面有缺陷,并且如果申请锁失败就会陷入阻塞等待的过程中。
对于一些场景,我们可以使用显示锁Lock

基本应用

Lock 的lock方法相当于进入同步块, unlock方法相当于退出同步块。支持跟内部锁同样的重入机制:

    public void displayLock(){
        Lock lock = new ReentrantLock();
        lock.lock();
        try{

        }finally{
            lock.unlock();
        }
    }

    public void innerLock(){
        Object lock = new Object();
        synchronized (lock){

        }
    }

可以看出内部锁的代码还有清爽一点, 显示锁需要在finally中释放

tryLock避免死锁

先看下死锁的情况:

public class TestLock {
    public static void main(String[] args) {
        final Accout accout1 = new Accout(20);
        final Accout accout2 = new Accout(20);
        final TestLock testLock = new TestLock();
        new Thread(){
            @Override
            public void run() {
                testLock.transferMoney(accout2, accout1, 10);
            }
        }.start();
        testLock.transferMoney(accout1, accout2, 10);
    }

    public void transferMoney(Accout accout1, Accout accout2, int money){
        synchronized (accout1){
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (accout2){
                accout1.add(money);
                accout2.decrease(money);
                System.out.println(accout1.getAccout());
                System.out.println(accout2.getAccout());
            }
        }
    }
}

class Accout{
    public Accout(int accout){
        this.accout = accout;
    }

    public Lock lock = new ReentrantLock();
    private int accout;
    public void add(int money){
        accout += money;
    }

    public void decrease(int money){
        accout -= money;
    }

    public int getAccout() {
        return accout;
    }
}

改为不会死锁的情况:

public void transferMoney(Accout accout1, Accout accout2, int money){
        if(accout1.lock.tryLock()){
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(accout2.lock.tryLock()){
                accout1.add(money);
                accout2.decrease(money);
                System.out.println(accout1.getAccout());
                System.out.println(accout2.getAccout());
                accout2.lock.unlock();      // sould be in finally block
            }
            accout1.lock.unlock();      // sould be in finally block
        }
    }

如上其实主要是利用了tryLock当获取不到锁就返回false的特性。另外tryLock还能设置超时时间,指定时间得不到锁才返回false.

tryLock可中断锁

当代码遇到锁标记有很多种选择,获得锁,如果锁不再了等待锁,重入进该锁, 响应中断。
通常的锁在需要等待的时候并不会响应中断。
如下:

public class TestLock {

    public static void main(String[] args) {
        new TestLock().testUnInterrupt();
    }

    public void testUnInterrupt(){
        Lock lock = new ReentrantLock();
        ThreadTest t1 = new ThreadTest(lock, "t1");
        t1.start();
        ThreadTest t2 = new ThreadTest(lock, "t2");
        t2.start();
        t2.interrupt();
    }
}

class ThreadTest extends Thread{
    private Lock lock;
    public ThreadTest(Lock lock, String name){
        super(name);
        this.lock = lock;
    }
    @Override
    public void run(){
        lock.lock();
        System.out.println(getName() + "运行开始");
//        try {
//            lock.lockInterruptibly();
//        } catch (InterruptedException e) {
//            System.out.println(getName() + "锁等待被中断");
//            e.printStackTrace();
//        }
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            System.out.println(getName() + "休眠被中断");
//            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        System.out.println(getName() + "运行完成");
    }
}

使用lock()的返回结果:

t1运行开始
t2运行开始
t1运行完成
t2休眠被中断
t2运行完成

使用lockInterruptibly()的返回结果:

t1运行开始
t2运行开始
t2锁等待被中断

可见lockInterruptibly与普通锁的区别在于可以在锁等待的时候响应中断

lock和synchronized性能

在JDK6之前性能上Lock要好很多,但是JDK6之后改进了内部锁的算法,现在差不多

公平锁

ReentrantLock锁的构造器中可以选择是否公平,公平的意思就是开始执行的顺序将会按照锁等待的顺序。这样会造成性能下降,只有在对顺序敏感的时候才需要。

synchronized还是ReentrantLock

  • 性能上JDK1.6,ReentrantLock略好。
  • ReentrantLock可以支持复杂的语义。
  • ReentrantLock需要显示关闭
  • synchronized语法上更简单易用
    综上,其实应该更多的使用synchoronized,只有在有必要的时候才使用ReentrantLock

读写锁

普通的锁限制了并发性,对于数据无害的读-读操作也会出现锁竞争。这个时候可以使用读写锁
读写锁的一些特性:
- 基本功能就是有写入的时候要等写完完成才能再执行写入,如果没有写入的时候读是可以并发的
- 可降级不可以升级,就是读解锁前如果获取写锁会死锁,但是写锁中是可以获取读锁的
- 各锁单独可重入
如下:

public class TestLock {

    public static void main(String[] args) {
        final Cache cache = new Cache();
        new Thread(){
            @Override
            public void run() {
                cache.add("1");
                cache.add("2");
                cache.add("3");
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                cache.get();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                cache.get();
            }
        }.start();
    }
}

class Cache{
    ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    List<String> datas = new ArrayList<String>();

    public void add(String data){
        rwl.writeLock().lock();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        datas.add(data);
        System.out.println("添加" + data);
        rwl.writeLock().unlock();
    }

    public String get(){
        rwl.readLock().lock();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String data = datas.get(0);
        datas.remove(0);
        rwl.readLock().unlock();
        System.out.println("获得" + data);
        return data;
    }
}

可以验证写锁互斥,读写并发。

时间: 2024-12-24 20:56:14

并发编程12-显示锁的相关文章

Java并发编程之显示锁ReentrantLock和ReadWriteLock读写锁_java

在Java5.0之前,只有synchronized(内置锁)和volatile. Java5.0后引入了显示锁ReentrantLock. ReentrantLock概况 ReentrantLock是可重入的锁,它不同于内置锁, 它在每次使用都需要显示的加锁和解锁, 而且提供了更高级的特性:公平锁, 定时锁, 有条件锁, 可轮询锁, 可中断锁. 可以有效避免死锁的活跃性问题.ReentrantLock实现了 Lock接口: 复制代码 代码如下:   public interface Lock {

《GO并发编程实战》—— 锁的使用

声明:本文是<Go并发编程实战>的样章,感谢图灵授权并发编程网站发布样章,禁止以任何形式转载此文. 在本节,我们对Go语言所提供的与锁有关的API进行说明.这包括了互斥锁和读写锁.我们在第6章描述过互斥锁,但却没有提到过读写锁.这两种锁对于传统的并发程序来说都是非常常用和重要的. 互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开方法--Lock和U

GO语言并发编程之互斥锁、读写锁详解_Golang

在本节,我们对Go语言所提供的与锁有关的API进行说明.这包括了互斥锁和读写锁.我们在第6章描述过互斥锁,但却没有提到过读写锁.这两种锁对于传统的并发程序来说都是非常常用和重要的. 一.互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开方法--Lock和Unlock.顾名思义,前者被用于锁定当前的互斥量,而后者则被用来对当前的互斥量进行解锁. 类型sy

《Java并发编程从入门到精通》显示锁Lock和ReentrantLock

作者:张振华    购买链接:天猫商城  JD商城  当当书店   显示锁Lock和ReentrantLock Lock是一个接口提供了无条件的.可轮询的.定时的.可中断的锁获取操作,所有加锁和解锁的方法都是显式的.包路径是:java.util.concurrent.locks.Lock.核心方法是lock(),unlock(),tryLock(),实现类有ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock

《Java并发编程的艺术》一一3.5 锁的内存语义

3.5 锁的内存语义 众所周知,锁可以让临界区互斥执行.这里将介绍锁的另一个同样重要,但常常被忽视的功能:锁的内存语义.3.5.1 锁的释放-获取建立的happens-before关系 锁是Java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 下面是锁释放-获取的示例代码. class MonitorExample { int a = 0; public synchronized void writer() { // 1 a++; //

android编程怎么在锁屏界面上显示文字

问题描述 android编程怎么在锁屏界面上显示文字 android编程中怎么在锁屏界面上显示文字,请大家帮我一下.

Java并发编程相关面试问题

基础概念 1.什么是原子操作?在Java Concurrency API中有哪些原子类(atomic classes)? 原子操作(atomic operation)意为"不可被中断的一个或一系列操作" .处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作. 在Java中可以通过锁和循环CAS的方式来实现原子操作. CAS操作--Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作. 原子操作是

《 Java并发编程从入门到精通》目录和序言

目 录 第一部分:线程并发基础   第1章 概念部分   1 1.1 CPU核心数.线程数 (主流cpu,线程数的大体情况说一下) 1 1.2 CPU时间片轮转机制 2 1.3 什么是进程和什么是线程 4 1.4 进程和线程的比较 5 1.5 什么是并行运行 7 1.6 什么是多并发运行 8 1.7 什么是吞吐量 9 1.8  多并发编程的意义及其好处和注意事项 10 1.9  分布式与并发运算关系 11 1.10 Linux和Window多并发可以采取不的一样机制(apache和tomcat?

使用Python中的greenlet包实现并发编程的入门教程_python

1   动机 greenlet 包是 Stackless 的副产品,其将微线程称为 "tasklet" .tasklet运行在伪并发中,使用channel进行同步数据交换. 一个"greenlet",是一个更加原始的微线程的概念,但是没有调度,或者叫做协程.这在你需要控制你的代码时很有用.你可以自己构造微线程的 调度器:也可以使用"greenlet"实现高级的控制流.例如可以重新创建构造器:不同于Python的构造器,我们的构造器可以嵌套的调用函