Java 多线程编程(锁优化)
转: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 多线程编程(锁优化)的更多相关文章
- java多线程编程——锁优化
并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问.加锁会带来性能上的损坏,似乎是众所周知的事情.然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程.如 ...
- Java多线程编程 — 锁优化
阅读目录 一.尽量不要锁住方法 二.缩小同步代码块,只锁数据 三.锁中尽量不要再包含锁 四.将锁私有化,在内部管理锁 五.进行适当的锁分解 正文 并发环境下进行编程时,需要使用锁机制来同步多线程间 ...
- Java多线程编程—锁优化
并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问.加锁会带来性能上的损坏,似乎是众所周知的事情.然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程.如 ...
- Java多线程之锁优化策略
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6561264.html 锁的优化策略 编码过程中可采取的锁优化的思路有以下几种: 1:减少锁持有时间 例如:对 ...
- Java多线程编程详解
转自:http://programming.iteye.com/blog/158568 线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Ja ...
- Java多线程编程实战指南(核心篇)读书笔记(五)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- java多线程编程01---------基本概念
一. java多线程编程基本概念--------基本概念 java多线程可以说是java基础中相对较难的部分,尤其是对于小白,次一系列文章的将会对多线程编程及其原理进行介绍,希望对正在多线程中碰壁的小 ...
- Java多线程编程那些事:volatile解惑--转
http://www.infoq.com/cn/articles/java-multi-thread-volatile/ 1. 前言 volatile关键字可能是Java开发人员“熟悉而又陌生”的一个 ...
- 《Java多线程编程实战指南(核心篇)》阅读笔记
<Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...
- Java多线程编程实战02:多线程编程模型
多线程编程模型 线程安全名词 串行.并发和并行 串行:一个人,将任务一个一个完成 并发:一个人,有策略地同时做多件事情 并行:多个人,每人做一个事情 竞态 名词 竞态:计算结果的正确性与时间有关的现象 ...
随机推荐
- cell上的按钮点击和左滑冲突
cell上的某个按钮的点击事件,当cell左滑的时候,只要活动的区域也在按钮上,那么按钮的点击事件也会调用. fix: 给按钮添加一个手势(TapGesture)那么当点击的时候就会响应点击手势的方法 ...
- WCF Restful Service
对 Web Services.WCF 和 Restful 的扫盲可参见:https://www.cnblogs.com/scy251147/p/3382436.html 关于之前对 WCF 的学习,可 ...
- [转]理解Linux的性能
来源:http://www.linuxfly.org/post/114/ [转]理解Linux的性能 项目中常遇到需要对目前运行的系统进行效率分析,或碰到客户咨询如何优化系统的效率问题.更 ...
- linux-修改树莓派分辨率
直接在树莓派下编辑 使用命令行来编辑配置文件 sudo nano /boot/config.txt 并在config.txt文件的最后加上以下代码即可 max_usb_current=1 hdmi_g ...
- Set的交集、差集踩坑记录
项目中我用到了Set的retainAll和removeAll两个方法取差集和交集. 用法网上都有,我也不展示了. 但是因为我是急着用,直接就照着写了,没想到出大问题了. 因为我的set是一个map的k ...
- rabbitmq数据备份与还原
一.场景 现在有服务器A和服务器B ,由于业务需要,要求把服务器A上mq的数据迁移到服务器B上,rabbitmq的数据包括元数据(RabbitMQ用户.vhost.队列.交换和绑定)和消息数据,而消息 ...
- TCP/IP结构图
IP: TCP: UDP:
- 移动分辨率和rpx
从一张设计图的实现说起,为什么模拟器下ip6的分辨率是375而设计图一般给750? 看上面的这张图,首先屏幕尺寸就是实际的物理尺寸,重点是分辨率pt和分辨率px,要回答这个问题,就要明白pt与px.那 ...
- @PostMapping和@PutMapping区别
@PostMapping和@PutMapping作用等同,都是用来向服务器提交信息.如果是添加信息,倾向于用@PostMapping,如果是更新信息,倾向于用@PutMapping.两者差别不是很明显 ...
- MySQL进阶13--常见六大约束: 非空/默认/主键/唯一约束/检查约束/外键约束--表级约束 / 列级约束
/* MySQL进阶13 常见六大约束: 1.not null 非空 2.default :默认值,用于保证该字段的默认值 ; 比如年龄:1900-10-10 3.primary key : 主键,用 ...