深入浅出 Java Concurrency (6): 锁机制 part 1[转]

前面的章节主要谈谈原子操作,至于与原子操作一些相关的问题或者说陷阱就放到最后的总结篇来整体说明。从这一章开始花少量的篇幅谈谈锁机制。

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

尽管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://www.cnblogs.com/0x2D-0x22/p/4138621.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432130.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

行为树

行为树(Behavior Tree)&#xff0c;有4大类型的Node&#xff1a; (1) Composites Node 组合节点&#xff0c;包括经典的&#xff1a;Sequence&#xff0c;Selector&#xff0c;Parallel * Selector Node 当执行本类型Node时&#xff0c;它将从begin到end迭代执行自己的Ch…

SCRUM 12.03

第二轮迭代从今天起正式开始了。12月3日&#xff0c;我们举行了一次组会。 第一轮迭代结束时&#xff0c;我们意识到第二轮迭代需要实现的功能主要如下&#xff1a; 在下次迭代的时候实现对多个网站的信息进行比较取最优惠的选择&#xff0c;目前我们劲针对了一个网站的信息进行…

阻止页面双击选中文本

轮播图实现中&#xff0c;发现当点击切换按钮时&#xff0c;如果用户点快乐&#xff0c;双击了按钮&#xff0c;就会选中文本。十分影响浏览效果&#xff0c;所以有了这篇文章。 IE以及谷歌下的解决办法&#xff1a; <div id"zell-carousel" class"zell-caro…

STL内嵌数据类型: value_type

使用stl库的时候一直对value_type这个东西理解的不是很好&#xff0c;可以说就是不理解。今天看了《STL源码剖析》才恍然大悟。这里稍作记录。 每个STL中的类都有value_type这种东西&#xff0c;通俗的说value_type 就是stl容器盛装的数据的数据类型&#xff0c;例如&#xff…

Boost.Python实现Python C/C++混合编程

导出函数 #include<string> #include<boost/python.hpp>using namespace std; using namespace boost::python;char const * greet() {return "hello,world";}BOOST_PYTHON_MODULE(hello_ext) {def("greet", greet); } import hello_ext pri…

swift UIActivityIndicatorView

// // ActivityIndicatorViewController.swift // UIControlDemo // // Created by on 14/12/1. // Copyright (c) 2014年 马大哈. All rights reserved. // import UIKit class ActivityIndicatorViewController: BaseViewController { var waitActivity:UIActivityIndi…

UE4打包后如何调试

在项目打包后发现有一个数组越界问题&#xff0c;然而无论是 Play in Editor或是 VS选为DebugGame后启动&#xff0c;游戏都没有任何问题&#xff0c;越界问题只在打包后出现。这里记录一下自己的Debug方法。 首先将项目以DebugGame配置打包&#xff1a; 更改打包配置&#xff…

asp.net 使用my97 datepicker实现前后两个日期的范围界定

说明&#xff1a;日期选择后&#xff0c;前面的日期小于等后面的日期&#xff0c;后面的日期大于等于前面的日期。点点看就知道了:) &#xff5e; 这里将周末日期不可选。代码如下&#xff1a; 1 <html xmlns"http://www.w3.org/1999/xhtml">2 <head runat…

james-2.3.2中的配置

james&#xff1a;1、解压缩2、先运行一遍3、修改配置 apps\james\SAR-INF\config.xml(1)postmaster(2)servername localhost --> abclocalhost j08.com --> abcj08.com 自动检查名称和IP设为false(3)注释掉RemoteAddrNotInNetwork(4)添加虚拟域名&#xff0c;指向localh…

Win7下安装配置gVim

本文根据vim官网的《Simple Steps to Help You Install gVim on Windows 7》【1】一文整理而成。 1. 下载gVim 在http://www.vim.org/download.php/pc 下找到“PC: MS-DOS and MS-Windows”&#xff0c;下载“gvim74.exe”【2】。 2. 安装gVim 1. 安装时可以选择更改目录&#…

ActiveReports 报表控件官方中文入门教程 (2)-创建、数据源、浏览以及发布

ActiveReports 报表控件官方中文入门教程 (2)-创建、数据源、浏览以及发布 原文:ActiveReports 报表控件官方中文入门教程 (2)-创建、数据源、浏览以及发布本篇文章将阐述首次使用 ActiveReports 报表控件 的方法&#xff0c;包括添加报表文件、绑定数据源以及如何发布报表等内…

C++ Lambda表达式基本用法

创建一个匿名函数并执行。采用的是配对的方括号[]。实例如下&#xff1a; 1 2 3 4 5 6 7 8 9 #include <iostream> using namespace std; int main() { []{ cout << "Hello,Worldn"; }(); } 我们也可以方便的将这个创建的匿名函数赋…

每日一测4(装箱与拆箱)

1、定义 装箱&#xff1a;用于在垃圾回收堆中存储值类型。装箱是值类型到 object 类型或到此值类型所实现的任何接口类型的隐式转换。 拆箱&#xff1a;从 object 类型到值类型或从接口类型到实现该接口的值类型的显式转换。 2、为什么要装箱&#xff1a; &#xff08;1&#x…

flex 正则表达式匹配规则

正则表达式扫描的优先级 1. 先根据空格&#xff0c;tab&#xff0c;回车分割字符串&#xff0c;作为正则表达式匹配的基础 2. 找到完全匹配的正则表达式&#xff0c;以排列在前面的优先级为高 3. 如果找不到匹配的表达式&#xff0c;那么从头开始截取字符串来查找合…

笔记本多硬盘win7下U盘安装Cnetos7引导问题!

CentOS7出来的时间已经不算短了&#xff0c;前段时间一直在虚拟机上使用&#xff0c;最近闲下来了&#xff0c;就像在自己的笔记本上装一个win7和Centos7的双系统体验以下实体机的效果&#xff0c;几经波折之后终于顺利的装上了WIN7和CentOS7的双系统&#xff01;在这里主要是想…

QString转char*的问题

QString tmp"abc"; char *p tmp.toLatin1().data()); QString tmp"abc"; char *p new char[1strlen(tmp.toLatin1().data())]; strcpy(p, tmp.toLatin1().data()); 运行模式两种方法都可以&#xff0c;调试模式&#xff0c;第一种方式无法得到QString的…

hdu 1870

水题。。。。 AC代码&#xff1a; #include <iostream> #include <queue> using namespace std; int main() {char str[1010];int i,k;while(scanf("%s",&str)!EOF){queue<char>q;for(i0;str[i]!B;i){if(str[i]()q.push(str[i]);if(str[i]))q.…

Linux Apache php MySQL 安装配置(Centos 6.4 yum安装)

一、yum准备 1.Yum&#xff08;全称为 Yellow dog Updater, Modified&#xff09;是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器。 基于RPM包管理&#xff0c;能够从指定的服务器自动下载RPM包并且安装&#xff0c;可以自动处理依赖性关系&#xff0c;并且一次安…

QComboBox 样式设置

QComboBox QAbstractItemView::item:hover {color: black;background-color: lightgreen; } 上面理论上是对鼠标划过的项目的样式设置&#xff0c;实际不管用 QComboBox QAbstractItemView { color:black; selection-background-color:yellow; background-color:white; } 上…