博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java多线程编程——锁优化
阅读量:5950 次
发布时间:2019-06-19

本文共 2924 字,大约阅读时间需要 9 分钟。

并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问。加锁会带来性能上的损坏,似乎是众所周知的事情。然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程。如果只有一个线程竞争锁,此时并不存在多线程竞争的情况,那么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 BUSSA extends Thread {
LockMethod lockMethod;
void deal(LockMethod lockMethod){
this.lockMethod = lockMethod;
}

@Overridepublic void run() {    super.run();    lockMethod.busiA();}123456复制代码

}

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

public class BUSSB extends Thread {
LockMethod lockMethod;
void deal(LockMethod lockMethod){
this.lockMethod = lockMethod;
}

@Overridepublic void run() {    super.run();    lockMethod.busiB();}123456复制代码

}

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();}1234567891011复制代码

}

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

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

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

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

这种情况经常发生,线程在得到了A锁之后,在同步方法块中调用了另外对象的同步方法,获得了第二个锁,这样可能导致一个调用堆栈中有多把锁的请求,多线程情况下可能会出现很复杂、难以分析的异常情况,导致死锁的发生。下面的代码显示了这种情况:

synchronized(A){

synchronized(B){

}  12复制代码

}

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

synchronized(A){

B  b = objArrayList.get(0);b.method(); //这是一个同步方法123复制代码

}

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

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

}}123复制代码

}

这种使用方式下,对象a的对象锁被外部所持有,让这把锁在外部多个地方被使用是比较危险的,对代码的逻辑流程阅读也造成困扰。一种更好的方式是在类的内部自己管理锁,外部需要同步方案时,也是通过接口方式来提供同步操作:

class A {
private Object lock = new Object();
public void method1() {
synchronized (lock){

}}123复制代码

}

class B {

public void method1() {
A a = new A();
a.method1();
}
}

具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加群。在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加群。如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的可以加群。java架构群:697579751 一起交流

转载地址:http://mpsxx.baihongyu.com/

你可能感兴趣的文章
快速添加百度网盘文件到Aria2 猴油脚本
查看>>
mac 无法登录mysql的解决办法
查看>>
Shiro权限判断异常之命名导致的subject.isPermitted 异常
查看>>
Hello world travels in cpp - 字符串(2)
查看>>
struts2自定义拦截器
查看>>
Eclipse安装adt插件后之后看不到andorid manger
查看>>
Kafka服务端脚本详解(1)一topics
查看>>
Zookeeper 集群安装配置,超详细,速度收藏!
查看>>
js中var self=this的解释
查看>>
js--字符串reverse
查看>>
面试题
查看>>
Facebook 接入之获取各个配置参数
查看>>
android ant Compile failed; see the compiler error
查看>>
项目经理笔记一
查看>>
通过IP地址获取地理位置
查看>>
计算机字符编码从0/1到UTF-8
查看>>
[原]Jenkins(三)---Jenkins初始配置和插件配置
查看>>
Cache Plugin 实现过程
查看>>
HBase Compaction详解
查看>>
TCP服务器端口转发: netsh
查看>>