转:https://mp.weixin.qq.com/s/lDuguEhuWiLY8ofBRy3tZA

并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问。

加锁会带来性能上的损坏,似乎是众所周知的事情。

然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程。

如果只有一个线程竞争锁,此时并不存在多线程竞争的情况,那么JVM会进行优化,那么这时加锁带来的性能消耗基本可以忽略。

因此,规范加锁的操作,优化锁的使用方法,避免不必要的线程竞争,不仅可以提高程序性能,也能避免不规范加锁可能造成线程死锁问题,提高程序健壮性。

下面阐述几种锁优化的思路。

 

01

 

尽量不要锁住方法

在普通成员函数上加锁时,线程获得的是该方法所在对象的对象锁。此时整个对象都会被锁住。

这也意味着,如果这个对象提供的多个同步方法是针对不同业务的,那么由于整个对象被锁住,一个业务业务在处理时,其他不相关的业务线程也必须wait。

下面的例子展示了这种情况:

LockMethod类包含两个同步方法,分别在两种业务处理中被调用:

public class LockMethod   {

public synchronized void busyA() {

for (int i = 0; i < 10000; i++) {

System.out.println(Thread.currentThread().getName() + "deal with bussiness A:"+i);

}

}

public synchronized void busyB() {

for (int i = 0; i < 10000; i++) {

System.out.println(Thread.currentThread().getName() + "deal with bussiness B:"+i);

}

}

}

BusyA是线程类,用来处理A业务,调用的是LockMethod的busyA()方法:

public class BusyA extends Thread {

LockMethod lockMethod;

void deal(LockMethod lockMethod){

this.lockMethod = lockMethod;

}

@Override

public void run() {

super.run();

lockMethod.busyA();

}

}

BusyB是线程类,用来处理B业务,调用的是LockMethod的busyB()方法:

public class BusyB extends Thread {

LockMethod lockMethod;

void deal(LockMethod lockMethod){

this.lockMethod = lockMethod;

}

@Override

public void run() {

super.run();

lockMethod.busyB();

}

}

TestLockMethod类,使用线程BusyA与BusyB进行业务处理:

public class TestLockMethod

public static void main(String[] args) {

LockMethod lockMethod = new LockMethod();

BUSSA bussa = new BUSSA();

BUSSB bussb = new BUSSB();

bussa.deal(lockMethod);

bussb.deal(lockMethod);

bussa.start();

bussb.start();

}

}

运行程序,可以看到在线程bussa 执行的过程中,bussb是不能够进入函数 busyB()的,因为此时lockMethod 的对象锁被线程bussa获取了。

 

02

 

缩小同步代码块,只锁数据

有时候为了编程方便,有些人会synchnoized很大的一块代码。

如果这个代码块中的某些操作与共享资源并不相关,那么应当把它们放到同步块外部,避免长时间的持有锁,造成其他线程一直处于等待状态。

尤其是一些循环操作、同步I/O操作。不止是在代码的行数范围上缩小同步块,在执行逻辑上,也应该缩小同步块。

 

例如多加一些条件判断,符合条件的再进行同步,而不是同步之后再进行条件判断,尽量减少不必要的进入同步块的逻辑。

03

 

锁中尽量不要再包含锁

这种情况经常发生,线程在得到了A锁之后,在同步方法块中调用了另外对象的同步方法,获得了第二个锁.

这样可能导致一个调用堆栈中有多把锁的请求,多线程情况下可能会出现很复杂、难以分析的异常情况,导致死锁的发生。

下面的代码显示了这种情况:

synchronized(A){

synchronized(B){

}

}

或是在同步块中调用了同步方法:

synchronized(A){

B  b = objArrayList.get(0);

b.method(); //这是一个同步方法

}

解决的办法是跳出来加锁,不要包含加锁:

{

B b = null;

synchronized(A){

b = objArrayList.get(0);

}

b.method();

}

04

 

将锁私有化,在内部管理锁

把锁作为一个私有的对象,外部不能拿到这个对象,更安全一些。

对象可能被其他线程直接进行加锁操作,此时线程便持有了该对象的对象锁。

例如下面这种情况:

class A {

public void method1() {

}

}

class B {

public void method1() {

A a = new A();

synchronized (a) { //直接进行加锁

      a.method1();

}

}

}

这种使用方式下,对象a的对象锁被外部所持有,让这把锁在外部多个地方被使用是比较危险的,对代码的逻辑流程阅读也造成困扰。

一种更好的方式是在类的内部自己管理锁,外部需要同步方案时,也是通过接口方式来提供同步操作:

class A {

private Object lock = new Object();

public void method1() {

synchronized (lock){

}

}

}

class B {

public void method1() {

A a = new A();

a.method1();

}

}

05

进行适当的锁分解

考虑下面这段程序:

public class GameServer {

public Map<String, List<Player>> tables = new HashMap<String, List<Player>>();

public void join(Player player, Table table) {

if (player.getAccountBalance() > table.getLimit()) {

synchronized (tables) {

List<Player> tablePlayers = tables.get(table.getId());

if (tablePlayers.size() < 9) {

tablePlayers.add(player);

}

}

}

}

public void leave(Player player, Table table) {/*省略*/}

public void createTable() {/*省略*/}

Java 多线程编程(锁优化)的更多相关文章

  1. java多线程编程——锁优化

    并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问.加锁会带来性能上的损坏,似乎是众所周知的事情.然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程.如 ...

  2. Java多线程编程 — 锁优化

      阅读目录 一.尽量不要锁住方法 二.缩小同步代码块,只锁数据 三.锁中尽量不要再包含锁 四.将锁私有化,在内部管理锁 五.进行适当的锁分解 正文 并发环境下进行编程时,需要使用锁机制来同步多线程间 ...

  3. Java多线程编程—锁优化

    并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问.加锁会带来性能上的损坏,似乎是众所周知的事情.然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程.如 ...

  4. Java多线程之锁优化策略

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6561264.html  锁的优化策略 编码过程中可采取的锁优化的思路有以下几种: 1:减少锁持有时间 例如:对 ...

  5. Java多线程编程详解

    转自:http://programming.iteye.com/blog/158568 线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Ja ...

  6. Java多线程编程实战指南(核心篇)读书笔记(五)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  7. java多线程编程01---------基本概念

    一. java多线程编程基本概念--------基本概念 java多线程可以说是java基础中相对较难的部分,尤其是对于小白,次一系列文章的将会对多线程编程及其原理进行介绍,希望对正在多线程中碰壁的小 ...

  8. Java多线程编程那些事:volatile解惑--转

    http://www.infoq.com/cn/articles/java-multi-thread-volatile/ 1. 前言 volatile关键字可能是Java开发人员“熟悉而又陌生”的一个 ...

  9. 《Java多线程编程实战指南(核心篇)》阅读笔记

    <Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...

  10. Java多线程编程实战02:多线程编程模型

    多线程编程模型 线程安全名词 串行.并发和并行 串行:一个人,将任务一个一个完成 并发:一个人,有策略地同时做多件事情 并行:多个人,每人做一个事情 竞态 名词 竞态:计算结果的正确性与时间有关的现象 ...

随机推荐

  1. VBA循环(十一)

    当需要多次执行一段代码时,就可以使用循环语句. 一般来说,语句是按顺序执行的:函数中的第一个语句首先执行,然后是第二个,依此类推. 编程语言提供了各种控制结构,允许更复杂的执行路径. 循环语句允许多次 ...

  2. EntityFramework进阶(一)- DbContext与ObjectContext互转

    本系列原创博客代码已在EntityFramework6.0.0测试通过,转载请标明出处 EF中我们常用的是DbContext作为上下文,如果要想获取元数据等信息还是要用到ObjectContext这个 ...

  3. iOS 毛玻璃效果的实现方法

    iOS开发中有的时候需要将图片设置模糊,来实现特定的效果获取更好的用户体验, iOS7之后半透明模糊效果得到大范围使用的比较大,现在也可以看到很多应用局部用到了图片模糊效果,可以通过高斯模糊和毛玻璃效 ...

  4. 大学毕业时成都SAP和深圳腾讯的Offer,我是怎么选择的

    2006年校园招聘,我拿到了SAP成都研究院和深圳腾讯的offer,而我最后选择了前者. 当时我主要是基于以下四点考虑. SAP成都有最优秀的人才 2006年SAP成都研究院刚刚成立,对人才的挑选非常 ...

  5. Linux Centos7 网络设置UUID号的修改方法

    1.生成一个UUID [root@localhost ~]# uuidgen ens33 223bdb47-2fed-4773-b984-5f5733e61904 2.UUID号写入网络配置文件ifc ...

  6. PE重装系统

    PE重装系统 PE: 含义:全称 Windows Preinstall Environment,即Windows 预安装环境 作用: 是一个用于Windows安装准备的最小操作系统,其实就是一个简易版 ...

  7. Linux学习之四-Linux发行版及版本比较

    Linux发行版及版本比较 三大家族: Fedora是基于RHEL,CentOS,Scientific Linux, 和Oracle Linux的社区版本.相比RHEL,Fedora打包了显著的更多的 ...

  8. 天兔 -Lepus 慢查询分析平台配置

    想要实现慢查询查询分析,需要在被监控端安装percona-toolkit工具.   1.被监控端安装软件包 yum -y install perl-IO-Socket-SSL yum -y insta ...

  9. mybatis3.0-[topic10-14] -全局配置文件_plugins插件简介/ typeHandlers_类型处理器简介 /enviroments_运行环境 /多数据库支持/mappers_sql映射注册

    mybatis3.0-全局配置文件_   下面为中文官网解释 全局配置文件的标签需要按如下定义的顺序: <!ELEMENT configuration (properties?, setting ...

  10. 0018SpringBoot连接docker中的mysql并使用druid数据源

    由于druid数据源自带监控功能,所以引用druid数据源 1.centos7中安装并启动docker 2.docker安装并启动mysql 3.pom.xml中引入druid依赖 4.applica ...