并发环境下进行编程时, 需要使用锁机制来同步多线程间的操作, 保证共享资源的互斥访问加锁会带来性能上的损坏, 似乎是众所周知的事情然而, 加锁本身不会带来多少的性能消耗, 性能主要是在线程的获取锁的过程如果只有一个线程竞争锁, 此时并不存在多线程竞争的情况, 那么 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; }
- @Override
- public void run() {
- super.run();
- lockMethod.busiA();
- }
- }
BUSSB 是线程类, 用来处理 B 业务, 调用的是 LockMethod 的 busiB() 方法: 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){
- }
- 1
- 2
- }
或是在同步块中调用了同步方法:
- 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(); } }
具有 1-5 工作经验的, 面对目前流行的技术不知从何下手, 需要突破技术瓶颈的可以加群在公司待久了, 过得很安逸, 但跳槽时面试碰壁需要在短时间内进修跳槽拿高薪的可以加群如果没有工作经验, 但基础非常扎实, 对 java 工作机制, 常用设计思想, 常用 java 开发框架掌握熟练的可以加群 java 架构群: 697579751 一起交流
来源: http://www.bubuko.com/infodetail-2509505.html