《深入浅出 Java Concurrency》—锁紧机构(一)Lock与ReentrantLock

《深入浅出 Java Concurrency》—锁紧机构(一)Lock与ReentrantLock

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

全栈程序员社区此处内容已经被作者隐藏,请输入验证码查看内容
验证码:
请关注本站微信公众号,回复“验证码”,获取验证码。在微信里搜索“全栈程序员社区”或者“www_javaforall_cn”或者微信扫描右侧二维码都可以关注本站微信公众号。

转会:http://www.blogjava.net/xylz/archive/2010/07/05/325274.html

前面的章节主要谈谈原子操作,至于与原子操作一些相关的问题或者说陷阱就放到最后的总结篇来总体说明。

从这一章開始花少量的篇幅谈谈锁机制。

上一个章节 中谈到了锁机制,而且针对于原子操作谈了一些相关的概念和设计思想。接下来的文章中。尽可能的深入研究锁机制,而且理解里面的原理和实际应用场合。

虽然synchronized在语法上已经足够简单了。在JDK 5之前仅仅能借助此实现,可是因为是独占锁。性能却不高,因此JDK 5以后就開始借助于JNI来完毕更高级的锁实现。

JDK 5中的锁是接口java.util.concurrent.locks.Lock 。

另外java.util.concurrent.locks.ReadWriteLock 提供了一对可供读写并发的锁。依据前面的规则,我们从java.util.concurrent.locks.Lock 的API開始。

 

void lock();

获取锁。

假设锁不可用。出于线程调度目的,将禁用当前线程。而且在获得锁之前。该线程将一直处于休眠状态。

void lockInterruptibly() throws InterruptedException;

假设当前线程未被中断。则获取锁。

假设锁可用。则获取锁,并马上返回。

假设锁不可用,出于线程调度目的,将禁用当前线程,而且在发生下面两种情况之中的一个曾经。该线程将一直处于休眠状态:

  • 锁由当前线程获得;或者
  • 其它某个线程中断 当前线程,而且支持对锁获取的中断。

假设当前线程:

  • 在进入此方法时已经设置了该线程的中断状态;或者
  • 在获取锁时被中断 ,而且支持对锁获取的中断,

则将抛出 
 
InterruptedException
 。并清除当前线程的已中断状态。

Condition newCondition();

返回绑定到此  Lock   实例的新  Condition   实例。下一小节中会重点谈Condition,此处不做过多的介绍。

boolean tryLock();

仅在调用时锁为空暇状态才获取该锁。

假设锁可用,则获取锁,并马上返回值  true 。假设锁不可用。则此方法将马上返回值  false 。

通常对于那些不是必须获取锁的操作可能实用。

boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

假设锁在给定的等待时间内空暇,而且当前线程未被中断,则获取锁。

假设锁可用,则此方法将马上返回值  true 。假设锁不可用,出于线程调度目的,将禁用当前线程,而且在发生下面三种情况之中的一个前,该线程将一直处于休眠状态:

  • 锁由当前线程获得;或者
  • 其它某个线程中断当前线程,而且支持对锁获取的中断;或者
  • 已超过指定的等待时间

假设获得了锁,则返回值  true 。

假设当前线程:

  • 在进入此方法时已经设置了该线程的中断状态;或者
  • 在获取锁时被中断,而且支持对锁获取的中断。

则将抛出 
 
InterruptedException
 。并会清除当前线程的已中断状态。

假设超过了指定的等待时间。则将返回值  false 。假设 time 小于等于 0,该方法将全然不等待。

void unlock();

释放锁。

相应于lock()、tryLock()、tryLock(xx)、lockInterruptibly()等操作,假设成功的话应该相应着一个unlock(),这样能够避免死锁或者资源浪费。

 

相对于比較空洞的API。来看一个实际的样例。以下的代码实现了一个类似于AtomicInteger的操作。

package xylz.study.concurrency.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AtomicIntegerWithLock {

    private int value;

    private Lock lock = new ReentrantLock();

    public AtomicIntegerWithLock() {
        super();
    }

    public AtomicIntegerWithLock(int value) {
        this.value = value;
    }

    public final int get() {
        lock.lock();
        try {
            return value;
        } finally {
            lock.unlock();
        }
    }

    public final void set(int newValue) {
        lock.lock();
        try {
            value = newValue;
        } finally {
            lock.unlock();
        }

    }

    public final int getAndSet(int newValue) {
        lock.lock();
        try {
            int ret = value;
            value = newValue;
            return ret;
        } finally {
            lock.unlock();
        }
    }

    public final boolean compareAndSet(int expect, int update) {
        lock.lock();
        try {
            if (value == expect) {
                value = update;
                return true;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    public final int getAndIncrement() {
        lock.lock();
        try {
            return value++;
        } finally {
            lock.unlock();
        }
    }

    public final int getAndDecrement() {
        lock.lock();
        try {
            return value–;
        } finally {
            lock.unlock();
        }
    }

    public final int incrementAndGet() {
        lock.lock();
        try {
            return ++value;
        } finally {
            lock.unlock();
        }
    }

    public final int decrementAndGet() {
        lock.lock();
        try {
            return –value;
        } finally {
            lock.unlock();
        }
    }

    public String toString() {
        return Integer.toString(get());
    }
}

AtomicIntegerWithLock 是线程安全的,此结构中大量使用了Lock对象的lock/unlock方法对。相同可以看到的是对于自增和自减操作使用了++/–。之所以可以保证线程安全,是由于Lock对象的lock()方法保证了仅仅有一个线程可以仅仅有此锁。须要说明的是对于不论什么一个lock()方法,都须要一个unlock()方法与之对于,通常情况下为了保证unlock方法总是可以得到运行,unlock方法被置于finally块中。另外这里使用了java.util.concurrent.locks.ReentrantLock.ReentrantLock 对象。下一个小节中会详细描写叙述此类作为Lock的唯一实现是怎样设计和实现的。

虽然synchronized实现Lock的同样语义,而且在语法上比Lock要简单多。可是前者却比后者的开销要大得多。做一个简单的測试。

public static void main(String[] args) throws Exception{
     final int max = 10;
     final int loopCount = 100000;
     long costTime = 0;
     for (int m = 0; m < max; m++) {
         long start1 = System.nanoTime();
         final AtomicIntegerWithLock value1 = new AtomicIntegerWithLock(0);
         Thread[] ts = new Thread[max];
         for(int i=0;i<max;i++) {
             ts[i] = new Thread() {
                 public void run() {
                     for (int i = 0; i < loopCount; i++) {
                         value1.incrementAndGet();
                     }
                 }
             };
         }
         for(Thread t:ts) {
             t.start();
         }
         for(Thread t:ts) {
             t.join();
         }
         long end1 = System.nanoTime();
         costTime += (end1-start1);
     }
     System.out.println(“cost1: ” + (costTime));
     //
     System.out.println();
     costTime = 0;
     //
     final Object lock = new Object();
     for (int m = 0; m < max; m++) {
         staticValue=0;
         long start1 = System.nanoTime();
         Thread[] ts = new Thread[max];
         for(int i=0;i<max;i++) {
             ts[i] = new Thread() {
                 public void run() {
                     for (int i = 0; i < loopCount; i++) {
                         synchronized(lock) {
                             ++staticValue;
                         }
                     }
                 }
             };
         }
         for(Thread t:ts) {
             t.start();
         }
         for(Thread t:ts) {
             t.join();
         }
         long end1 = System.nanoTime();
         costTime += (end1-start1);
     }
     //
     System.out.println(“cost2: ” + (costTime));
}

static int staticValue = 0;

 

在这个样例中每次启动10个线程,每一个线程计算100000次自增操作,反复測试10次,以下是某此測试的结果:

cost1: 624071136

cost2: 2057847833

虽然上面的例子是不是很正规的测试案例,然而,上述例子是为了说明,Lock性能比synchronized更好。那么假设可以随时使用Lock替代synchronized这是一个明智的选择。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/117878.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • APP测试基本流程以及APP测试要点梳理,保证您看了不后悔!

    APP测试基本流程以及APP测试要点梳理,保证您看了不后悔!前言:相信很多刚刚步入测试行业的小伙伴对于APP测试不是很熟悉,这次我为大家提供一篇宝藏文章,希望大家喜欢,谢谢!一、APP测试基本流程1、流程图2、测试周期测试周期可按项目的开发周期来确定测试时间,一般测试时间为两三周(即15个工作日),根据项目情况以及版本质量可适当缩短或延长测试时间。3、测试资源测试任务开始前,检查各项测试资源。–产品功能需求文档;–产品原型图;–产品效果图;–测试设备;–其他。4、日报及产品上线报告(内部报告机制)–测试人员每天需对所测项目发送测试日报。(

    2022年5月5日
    112
  • python3.8安装matplotlib_matplotlib画图

    python3.8安装matplotlib_matplotlib画图1.直接打开命令提示符(快捷键window+r)2.若提示安装失败(Python——Youareusingpipversion9.0.1,howeverversion10.0.1isavailable.),输入python-mpipinstall-Upipsetuptools进行升级。安装成功,则下图所示:3.安装成功后,输入pytho…

    2022年8月31日
    5
  • 开源报表编辑器 java_常用的6款Java开源报表制作工具[通俗易懂]

    开源报表编辑器 java_常用的6款Java开源报表制作工具[通俗易懂]本文为大家推荐6款常用的Java开源报表制作工具,供开发者学习、参考。1.Aspose.CellsforJasperReports一个基于Java的开源报表工具,它可以在Java环境下像其他IDE报表工具一样来制作报表,支持PDF、HTML、XLS、CSV和XML文件输出格式,是当前Java开发者最常用的报表工具。2.BIRT一个Eclipse-based开放源代码报表系统。它主要是用在基于J…

    2022年10月20日
    4
  • 确认新CEO及首次上市失败,比特大陆成立5年后的危机与未来 …

    确认新CEO及首次上市失败,比特大陆成立5年后的危机与未来 …

    2021年7月3日
    162
  • spring bean别名注解_java.lang.clonable是类

    spring bean别名注解_java.lang.clonable是类前言在spring容器中,允许通过名称或别名来获取bean,这个能力来自于顶层接口AliasRegistry,分析类下属的关系图,可以看到,几乎所有主要容器都直接或间接的实现了Alias

    2022年8月17日
    5
  • UML图之四——活动图

    UML图之四——活动图点击打开链接活动图是一种流程图,用来描述活动的序列,从一个活动到另一个活动的控制流。活动图的作用:描述用例,描述类的操作。 活动图的构成必要组成元素:1、活动:命令的执行,活动的进行。图符表示:2、状态:开始状态,结束状态。图符表示: 3、转移:活动之间,活动与状态之间的转换。4、判断:对一个动作或者状态进行判断,然后选择要执行的下一步操作。

    2022年5月5日
    41

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号