Java 多线程编程(锁优化)

转自: https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/94508955

扒一扒 JVM 的垃圾回收机制,拿大厂offer少不了它: https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/94508928

java基础-二分查找算法之JAVA实现: https://blog.csdn.net/weixin_42227576/article/details/94555441

# 国外大牛程序员开发的Windows10企业/专业版永久激活工具:  https://blog.csdn.net/u011333881/article/details/87891569

京东程序员写自动化程序,代替自己陪女友: https://blog.csdn.net/weixin_36841920/article/details/91357118

关于微服务的7个疑问和解答: https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/94241424

什么是DevOps的基石?为什么顶级公司都离不开它:  https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/93988392

springmvc处理流程初步分析:  https://blog.csdn.net/horse_xiao/article/details/94437193

数据分析惯用的5种思维方法: https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/94366983

玩转数据结构 java描述 一 概况: https://blog.csdn.net/u012654756/article/details/91884600

阿里、百度、腾讯都选择 Flink,它到底有什么魔力: https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/94241386

Java程序员必须了解的JVM性能调优知识,全都在这里了: 

https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/94241437

计算机图形学debug记录: https://blog.csdn.net/weixin_36313227/article/details/92441050

2019年这50个Kafka面试题,你知道答案么: https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/93988404

年薪百万的程序员:我的世界你真的懂吗: https://blog.csdn.net/weixin_42831704/article/details/90764181

Linux命令行下如何通过命令查看一个大日志文件中关键字的上下文信息: https://blog.csdn.net/feinifi/article/details/94555587

SpringCloud 之 Druid 配置及数据库密码加密: https://blog.csdn.net/qq_37143673/article/details/94397090

一种基于linux系统的精准流量统计方法: https://blog.csdn.net/li_wen01/article/details/93597936

Java-RPC通信--HSF框架: https://blog.csdn.net/weixin_39984161/article/details/89002927

毋庸置疑,容器带来改变: https://blog.csdn.net/sch881226/article/details/94445486

深入理解Spring IOC 之@Configuration: https://blog.csdn.net/chaosbead/article/details/94435226

Linux上,最常用的一批命令解析(10年精选): https://blog.csdn.net/lycyingO/article/details/93816271

200行代码实现一个滑动验证码: https://blog.csdn.net/dQCFKyQDXYm3F8rB0/article/details/94416490

前后端分离时代,Java 程序员的变与不变: https://blog.csdn.net/u012702547/article/details/94381774

Linux 启动流程详细解析: https://blog.csdn.net/Ki8Qzvka6Gz4n450m/article/details/94366674

Docker与Jib(maven插件版)实战: https://blog.csdn.net/boling_cavalry/article/details/94355659

以太坊V神首次点评Facebook Libra: https://blog.csdn.net/han0373/article/details/94381728

laravel+ajax分页操作: https://blog.csdn.net/qq_45191205/article/details/91614860

Android与单片机 | 开发板 | 智能硬件 | 智能设备 | 数据协议 |开发总结: https://blog.csdn.net/a_zhon/article/details/91975201

感受程序员的浪漫情怀-Github婚恋项目精选: https://blog.csdn.net/BEYONDMA/article/details/94132506

GitHub 上有哪些一般人也可以用的项目:  https://blog.csdn.net/sinat_33224091/article/details/93997954

用jquery-easyui的布局layout写后台管理页面: https://blog.csdn.net/weixin_41049260/article/details/91612966

跨域问题与SpringBoot解决方案: https://blog.csdn.net/Java_No2/article/details/91612669

MySQL 性能调优——数据库的分库分表: https://blog.csdn.net/smartbetter/article/details/93541792

 

java Web的编码问题:  https://blog.csdn.net/weixin_44634893/article/details/91613145

Echarts 动态更新散点图: https://blog.csdn.net/qq_15046309/article/details/91615294

SpringBoot2.0 整合 SpringSession 最简操作: https://blog.csdn.net/ljk126wy/article/details/93971421

layui数据表格如何加工具栏: https://blog.csdn.net/weixin_37914752/article/details/91884949

2019 全球科技行业薪资报告出炉:全栈开发受热捧,40 岁以上程序员收入最高: https://blog.csdn.net/csdnnews/article/details/94240282

 

 

 

2019年07月02日 11:52:24 架构师小秘圈 阅读数 4

640?wx_fmt=png

来自:老九学堂

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

 

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

 

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

 

如果只有一个线程竞争锁,此时并不存在多线程竞争的情况,那么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() {/*省略*/} 

 

长按订阅更多精彩▼

640?wx_fmt=jpeg

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值