synchronized关键字是java并发编程中常使用的同步锁,用于锁住方法或者代码块,锁代码块时可以是synchronized(this){}、synchronized(Object){}、synchronized(类class){}。

  当锁住的内容执行完或者在执行过程中抛出异常,才会自动释放锁。如果想手动释放锁,需要调用锁住的对象的wait()方法释放掉锁并且置于等待状态,切换到其他线程运行,而notify()方法只是唤醒一个调用了该对象wait()方法的其他线程,但不会释放锁,选择顺序也不由代码控制,由虚拟机实现。因此,对象的wait()、notify()、notifyAll()方法只能是配合synchronized关键字使用的,来完成线程间的调度。

  其中锁住方法等同于synchronized(this){方法的所有代码作为代码块},如下:

  public synchronized void test() {

  ...

  }

  等同于

  public void test() {

  synchronized (this) {

  ...

  }

  }

  上面的例子锁住的是该类的对象,如果锁住的是个静态的方法,我们知道静态方法是属于类的而不属于对象的,所以,synchronized修饰的静态方法锁定的是这个类的所有对象,即就算是两个实例对象,只要他们都是这个类的,那都会锁住。

  public synchronized static void test() {

  ...

  }

  等同于

  public static void test() {

  synchronized (所在类.class) {

  ...

  }

  }

  无论是锁方法还是锁代码块,无论锁代码块时的参考对象是什么,只要记住一个原则就一目了然了,那就是当参考对象相同时,同步锁才起作用,否则锁不会互斥,可以并发执行。

  synchronized(this)表示当前类的对象实例相同时锁起作用,synchronized(Object)表示该Object对象相同时锁起作用,synchronized(类class)表示当都是该class类时锁起作用。

  举一个简单的例子:

  public class TestController {

  public class Task implements Runnable{

  private String str;

  Task(String str){

  this.str=str;

  }

  @Override

  public void run() {

  synchronized (str) {

  try {

  Thread.sleep(3000l);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  System.out.println(str);

  }

  }

  }

  public static void main(String[] args) throws InterruptedException {

  TestController testController = new TestController();

  Thread thread1 = new Thread(testController.new Task("1"));

  Thread thread2 = new Thread(testController.new Task("1"));

  thread1.start();

  thread2.start();

  }

  }

  上述代码,参照对象str都是"1",在java中,String字符串如果通过this.str="1"这样的方式赋值就等同于str=String.valueOf("1"),如果字符串"1"之前已经初始化过,那就会直接拿之前的,所以是同一个对象。根据上面介绍的原则,那锁就会起作用,所以结果是3秒之后输出1,再过3秒再输出1。

  如果把thread2改成

  Thread thread2 = new Thread(testController.new Task("2"));

  这时参考对象一个是"1",另一个是"2",不是同一个对象,所以锁不会互斥,就不会起作用,所以结果是3秒后几乎同时输出1和2。

  以上都是多个线程同时调用同一个方法,如果调用不同的方法呢?

  public class Test{

  public synchronized void m1(){

  System.out.println("m1 running...");

  try {

  Thread.sleep(3000l);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  System.out.println("m1 end");

  }

  public synchronized void m2(){

  System.out.println("m2 running...");

  System.out.println("m2 end");

  }

  public static void main(String[] args) {

  Test test = new Test();

  new Thread(new Runnable() {

  @Override

  public void run() {

  test.m1();

  }

  }).start();

  new Thread(new Runnable() {

  @Override

  public void run() {

  test.m2();

  }

  }).start();

  }

  }

  上面代码的输出结果是:

  m1 running...

  //过3秒

  m1 end

  m2 running...

  m2 end

  上面就说过synchronized修饰在方法上等同于synchronized(this){方法的所有代码作为代码块},而this就代表是对象,也就是说,第一个Thread获得的是test对象的锁,因为对象都是同一个test,所以第二个Thread无法获取到锁,而被阻塞。

  把上面的例子改造成如下:无锡看男科哪家好 http://www.jzspfk.com/

  private String str = "1";

  public void m1(){

  synchronized(str){

  System.out.println("m1 running...");

  try {

  Thread.sleep(3000l);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  System.out.println("m1 end");

  }

  }

  public void m2(){

  synchronized(str){

  System.out.println("m2 running...");

  System.out.println("m2 end");

  }

  }

  第一个Thread调用m1()时获取到的是对象str的锁,第二个Thread调用m2()时也需要获取对象str的锁,而且因为是同一个Test对象,所以两个str也是同一个对象,所以第二个Thread会因为获取不到锁而被阻塞,输出结果和之前的例子一样。

  如果再把上面的例子改造成如下:

  public class M1 {

  public void m(String str){

  synchronized (str) {

  System.out.println("m1 runing");

  try {

  Thread.sleep(3000l);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  System.out.println("m1 end");

  }

  }

  }

  public class M2 {

  public void m(String str){

  synchronized (str) {

  System.out.println("m2 runing");

  System.out.println("m2 end");

  }

  }

  }

  public class Test {

  public static void main(String[] args) {

  String str = "1";

  new Thread(new Runnable() {

  @Override

  public void run() {

  new M1().m(str);

  }

  }).start();

  new Thread(new Runnable() {

  @Override

  public void run() {

  new M2().m(str);

  }

  }).start();

  }

  }

  这次调用的方法在两个类里面,但是结果和之前的两个例子是一样的,因为锁住的都是传进来的str对象,同一个对象只有一把锁,第一个Thread拿了,第二个Thread就只能等待。

  如果想看其他详细的例子说明可参考:http://blog.csdn.net/luoweifu/article/details/46613015

  总结:

  A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。

  B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

  C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

最新文章

  1. 关于apue.3e中apue.h的使用
  2. [LeetCode] Can I Win 我能赢吗
  3. SQL Server 堆表行存储大小(Record Size)
  4. 获取Executor提交的并发执行的任务返回结果的两种方式/ExecutorCompletionService使用
  5. 【数论】Miller_Rabin
  6. uva 120 stacks of flapjacks ——yhx
  7. 如何在win7下配置IIS?
  8. FTP操作
  9. Linux进程栈和线程栈
  10. maven依赖传递关系
  11. navicat for mysql (10038)如何解决,远程无法连接问题
  12. MVC5 学习笔记2
  13. 用标准版的Eclipse搭建PHP环境
  14. java和.net 处理任意格式日期字符串转日期类型,
  15. silverlight 调试问题
  16. iOS CATransition 动画的简单使用
  17. js前段开发工具
  18. CSS遮罩mask
  19. 给Access数据库文件减肥
  20. 迭代器与泛型for

热门文章

  1. regexp_replace
  2. SNF-软件开发机器人-免费-火爆登场-程序下载及实战配套教程免费发放
  3. 如何使用phantomJS来模拟一个HTML元素的鼠标悬停
  4. hangfire 实现已完成的job设置过期,防止数据无限增长
  5. C# 取得某月的最后一天和第一天
  6. roboware 常见操作和问题
  7. java 欢迎页 主页 设置为servlet的方法
  8. python网络编程 - tcp
  9. 01-Windows Server 2012的配置与部署
  10. 利用MySQL存储过程批量插入100W条测试数据