正文

并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问。加锁会带来性能上的损坏,似乎是众所周知的事情。然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程。如果只有一个线程竞争锁,此时并不存在多线程竞争的情况,那么JVM会进行优化,那么这时加锁带来的性能消耗基本可以忽略。因此,规范加锁的操作,优化锁的使用方法,避免不必要的线程竞争,不仅可以提高程序性能,也能避免不规范加锁可能造成线程死锁问题,提高程序健壮性。下面阐述几种锁优化的思路。

一、尽量不要锁住方法

在普通成员函数上加锁时,线程获得的是该方法所在对象的对象锁。此时整个对象都会被锁住。这也意味着,如果这个对象提供的多个同步方法是针对不同业务的,那么由于整个对象被锁住,一个业务业务在处理时,其他不相关的业务线程也必须wait。下面的例子展示了这种情况:

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

public class LockMethod {

public synchronized void busiA() {

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

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

}

}

public synchronized void busiB() {

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

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

}

}

}

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

public class BUSSB extends Thread {

LockMethod lockMethod;

void deal(LockMethod lockMethod){

this.lockMethod = lockMethod;

}

@Override

public void run() {

super.run();

lockMethod.busiB();

}

}

TestLockMethod类,使用线程BUSSA与BUSSB进行业务处理:

public class TestLockMethod extends Thread {

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是不能够进入函数 busiB()的,因为此时lockMethod 的对象锁被线程bussa获取了。

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

有时候为了编程方便,有些人会synchnoized很大的一块代码,如果这个代码块中的某些操作与共享资源并不相关,那么应当把它们放到同步块外部,避免长时间的持有锁,造成其他线程一直处于等待状态。尤其是一些循环操作、同步I/O操作。不止是在代码的行数范围上缩小同步块,在执行逻辑上,也应该缩小同步块,例如多加一些条件判断,符合条件的再进行同步,而不是同步之后再进行条件判断,尽量减少不必要的进入同步块的逻辑。

三、锁中尽量不要再包含锁

这种情况经常发生,线程在得到了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();

}

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

把锁作为一个私有的对象,外部不能拿到这个对象,更安全一些。对象可能被其他线程直接进行加锁操作,此时线程便持有了该对象的对象锁,例如下面这种情况:

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();

}

}

五、进行适当的锁分解

考虑下面这段程序:

public class GameServer {

public Map> tables = new HashMap>();

public void join(Player player, Table table) {

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

synchronized (tables) {

List tablePlayers = tables.get(table.getId());

if (tablePlayers.size() < 9) {

tablePlayers.add(player);

}

}

}

}

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

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

public void destroyTable(Table table) {/*省略*/}

}

在这个例子中,join方法只使用一个同步锁,来获取tables中的List对象,然后判断玩家数量是不是小于9,如果是,就调增加一个玩家。当有成千上万个List存在tables中时,对tables锁的竞争将非常激烈。在这里,我们可以考虑进行锁的分解:快速取出数据之后,对List对象进行加锁,让其他线程可快速竞争获得tables对象锁:

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()) {

List < Player > tablePlayers = null;

synchronized(tables) {

tablePlayers = tables.get(table.getId());

}

synchronized(tablePlayers) {

if (tablePlayers.size() < 9) {

tablePlayers.add(player);

}

}

}

}

public void leave(Player player, Table table) {

/*省略*/

}

public void createTable() {

/*省略*/

}

public void destroyTable(Table table) {

/*省略*/

}

}

java单线程上锁_关于Java多线程编程锁优化的深入学习相关推荐

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

    2019独角兽企业重金招聘Python工程师标准>>> 阅读目录 一.尽量不要锁住方法 二.缩小同步代码块,只锁数据 三.锁中尽量不要再包含锁 四.将锁私有化,在内部管理锁 五.进行 ...

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

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

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

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

  4. java内存屏障_内存屏障 | 并发编程网 – ifeve.com

    本文我将和大家讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见的一项技术. CPU使用了很多优化技术来实现一个目标:CPU执行单元的速度要远 ...

  5. java进度条_自学java你需要知道的,适合编程小白

    1. java学习网站 之前在很多学习网站上学习过Java,踩过很多坑,今天给大家推荐一个比较好用的Java学习网站,希望大家能少踩坑 Java教程 | 项目实践一站式java学习 这个网站的针对性强 ...

  6. java核心教程_核心Java教程

    java核心教程 Welcome to Core Java Tutorial. I have written a lot on Core Java and Java EE frameworks. Th ...

  7. java大厂技术面试第二课 多线程和锁

    第03讲:线程的状态有哪些?它是如何工作的? 线程(Thread)是并发编程的基础,也是程序执行的最小单元,它依托进程而存在.一个进程中可以包含多个线程,多线程可以共享一块内存空间和一组系统资源,因此 ...

  8. java 分割一个_分割java

    [java]分割字符串工具类,霸气 jdk自带的 java 分割字符串,分割string,可以根据多个条件去分割.比如逗号,分号,逗号或者分号. 比如一个字符串:"abc,def;gh,ij ...

  9. java linux 管理系统_用Java开发一个本地服务管理软件

    使用Java开发一个本机服务管理程序,能够控制本机Tomcat.Apache服务的开启和关闭,图形界面控制.用户可以自己扩展其他服务,用来学习图形界面编程.多线程.事件响应等都不错. 一.最终界面 先 ...

最新文章

  1. GridView中使用DataFromatString
  2. 30.IntellJ Idea 导入已存在的Maven项目
  3. Ubuntu 9.10下在右键中添加以管理员身份打开,在终端中打开
  4. mongodb type
  5. IDC机房运行安全评测怎么做,自建IDC机房评测材料准备要点
  6. springboot @ConfigurationProperties注入属性流程
  7. mysql 图形插件_对我来说数据库图形化管理工具用这个足够了
  8. Linux中source命令的用法:修改环境变量之后立即生效
  9. R语言入门2---R语言基础绘图
  10. 银河麒麟V10入选2020中国十大科技新闻
  11. Linux下python安装升级详细步骤 | Python2 升级 Python3
  12. Linux 如何通过命令仅获取IP地址
  13. android 平板怎么截图,平板电脑怎么截图 平板电脑截图方法【详解】
  14. ffmpeg (二):ffmpeg结合SDL2.0解码视频流
  15. css3弹性盒模型flex快速入门与上手(align-content与align-items)
  16. Android系统Google TTS 语音数据包安装步骤
  17. 达梦数据库学习之备份还原
  18. CAD异常eNotOpenForWrite
  19. 三、大数据时代下的SQL Server第三方负载均衡方案----Moebius测试
  20. 科学养生:揭秘世界上最健康的作息时间表

热门文章

  1. 分析Linux文件rwx属性的含义
  2. MySQL运维常用系统命令
  3. mysql中文编码问题
  4. 宁波政务云资源的介绍与申请
  5. 修改Navicat数据库自动备份目录
  6. spring框架的引入
  7. php找出函数定义位置,WordPress如何快速定位PHP函数所在文件位置及代码行号?
  8. linux shmmax单位,Linux核心参数Shmmax,shmall,shmni
  9. tkmybatis 子查询_真假童子命符箓道长教你如何查询
  10. 线性代数向量内积_向量的外积| 使用Python的线性代数