关闭

Java中synchronized的用法

发表于:2016-10-09 09:34

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

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

  调用代码:
  Account account = new Account("zhang san", 10000.0f);
  AccountOperator accountOperator = new AccountOperator(account);
  final int THREAD_NUM = 5;
  Thread threads[] = new Thread[THREAD_NUM];
  for (int i = 0; i < THREAD_NUM; i ++) {
  threads[i] = new Thread(accountOperator, "Thread" + i);
  threads[i].start();
  }
  结果如下:
  Thread3:10000.0
  Thread2:10000.0
  Thread1:10000.0
  Thread4:10000.0
  Thread0:10000.0
  在AccountOperator 类中的run方法里,我们用synchronized 给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞,直到该线程访问account对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。
  当有一个明确的对象作为锁时,就可以用类似下面这样的方式写程序。
  public void method3(SomeObject obj)
  {
  //obj 锁定的对象
  synchronized(obj)
  {
  // todo
  }
  }
  当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:
  class Test implements Runnable
  {
  private byte[] lock = new byte[0];  // 特殊的instance变量
  public void method()
  {
  synchronized(lock) {
  // todo 同步代码块
  }
  }
  public void run() {
  }
  }
  说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
  修饰一个方法
  Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){//todo}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。如将【Demo1】中的run方法改成如下的方式,实现的效果一样。
  *【Demo4】:synchronized修饰一个方法
  public synchronized void run() {
  for (int i = 0; i < 5; i ++) {
  try {
  System.out.println(Thread.currentThread().getName() + ":" + (count++));
  Thread.sleep(100);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  }
  Synchronized作用于整个方法的写法。
  写法一:
  public synchronized void method()
  {
  // todo
  }
  写法二:
  public void method()
  {
  synchronized(this) {
  // todo
  }
  }
  写法一修饰的是一个方法,写法二修饰的是一个代码块,但写法一与写法二是等价的,都是锁定了整个方法时的内容。
  在用synchronized修饰方法时要注意以下几点:
  1. synchronized关键字不能继承。
  虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。这两种方式的例子代码如下:
  在子类方法中加上synchronized关键字
  class Parent {
  public synchronized void method() { }
  }
  class Child extends Parent {
  public synchronized void method() { }
  }
  在子类方法中调用父类的同步方法
  class Parent {
  public synchronized void method() {   }
  }
  class Child extends Parent {
  public void method() { super.method();   }
  }
  在定义接口方法时不能使用synchronized关键字。
  构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
  修饰一个静态的方法
  Synchronized也可修饰一个静态方法,用法如下:
  public synchronized static void method() {
  // todo
  }
  我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。我们对Demo1进行一些修改如下:
  【Demo5】:synchronized修饰静态方法
/**
* 同步线程
*/
class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
public synchronized static void method() {
for (int i = 0; i < 5; i ++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void run() {
method();
}
}
  调用代码:
  SyncThread syncThread1 = new SyncThread();
  SyncThread syncThread2 = new SyncThread();
  Thread thread1 = new Thread(syncThread1, "SyncThread1");
  Thread thread2 = new Thread(syncThread2, "SyncThread2");
  thread1.start();
  thread2.start();
  结果如下:
  SyncThread1:0
  SyncThread1:1
  SyncThread1:2
  SyncThread1:3
  SyncThread1:4
  SyncThread2:5
  SyncThread2:6
  SyncThread2:7
  SyncThread2:8
  SyncThread2:9
  syncThread1和syncThread2是SyncThread的两个对象,但在thread1和thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁。这与Demo1是不同的。
  修饰一个类
  Synchronized还可作用于一个类,用法如下:
  class ClassName {
  public void method() {
  synchronized(ClassName.class) {
  // todo
  }
  }
  }
  我们把Demo5再作一些修改。
  【Demo6】:修饰一个类
/**
* 同步线程
*/
class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
public static void method() {
synchronized(SyncThread.class) {
for (int i = 0; i < 5; i ++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public synchronized void run() {
method();
}
}
  其效果和【Demo5】是一样的,synchronized作用于一个类T时,是给这个类T加锁,T的所有对象用的是同一把锁。
  总结:
  A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
  B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
  C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

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

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

沪ICP备05003035号

沪公网安备 31010102002173号