关闭

Java线程安全总结

发表于:2011-10-18 09:28

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:jameswxx    来源:51Testing软件测试网采编

  synchronized关键字

  上面说了,java用synchronized关键字做为多线程并发环境的执行有序性的保证手段之一。当一段代码会修改共享变量,这一段代码成为互斥区或临界区,为了保证共享变量的正确性,synchronized标示了临界区。典型的用法如下:

  Java代码

synchronized(锁){  
     临界区代码  
}

  为了保证银行账户的安全,可以操作账户的方法如下:

  Java代码

public synchronized void add(int num) {  
     balance = balance + num;  
}  
public synchronized void withdraw(int num) {  
     balance = balance - num;  
}

  刚才不是说了synchronized的用法是这样的吗:

  Java代码

synchronized(锁){  
临界区代码  
}

  那么对于public synchronized void add(int num)这种情况,意味着什么呢?其实这种情况,锁就是这个方法所在的对象。同理,如果方法是public  static synchronized void add(int num),那么锁就是这个方法所在的class。

  理论上,每个对象都可以做为锁,但一个对象做为锁时,应该被多个线程共享,这样才显得有意义,在并发环境下,一个没有共享的对象作为锁是没有意义的。假如有这样的代码:

  Java代码

public class ThreadTest{  
  public void test(){  
     Object lock=new Object();  
     synchronized (lock){  
        //do something  
     }  
  }  
}

  lock变量作为一个锁存在根本没有意义,因为它根本不是共享对象,每个线程进来都会执行Object lock=new Object();每个线程都有自己的lock,根本不存在锁竞争。

  每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个被线程被唤醒(notify)后,才会进入到就绪队列,等待cpu的调度。当一开始线程a第一次执行account.add方法时,jvm会检查锁对象account的就绪队列是否已经有线程在等待,如果有则表明account的锁已经被占用了,由于是第一次运行,account的就绪队列为空,所以线程a获得了锁,执行account.add方法。如果恰好在这个时候,线程b要执行account.withdraw方法,因为线程a已经获得了锁还没有释放,所以线程b要进入account的就绪队列,等到得到锁后才可以执行。

  一个线程执行临界区代码过程如下:

  1、获得同步锁
  2、清空工作内存
  3、从主存拷贝变量副本到工作内存
  4、对这些变量计算
  5、将变量从工作内存写回到主存
  6、释放锁

  可见,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。

42/4<1234>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号