文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java多线程编程中的锁有什么用

2023-06-17 00:02

关注

这篇文章主要讲解了“Java多线程编程中的锁有什么用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java多线程编程中的锁有什么用”吧!

阅读目录

一、尽量不要锁住方法

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

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

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

五、进行适当的锁分解

正文

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

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

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

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<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() {}    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多线程编程中的锁有什么用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯