Java 并发/多线程教程(十二)-JAVA同步块

本系列译自jakob jenkov的Java并发多线程教程,个人觉得很有收获。由于个人水平有限,不对之处还望矫正!

一个Java同步块标记一个方法或一个代码块作为同步。可以使用Java同步块来避免竞态条件。

java同步关键字

      在Java中同步的块被标记为Synchronized关键字。Java中的同步块在某些对象上是同步的。在同一对象上同步的所有同步块只能在同一时间内执行一个线程。所有试图进入同步块的其他线程都被阻塞,直到同步块中的线程退出该块.

Synchronized关键字可以用来标记四种不同类型的块:

1、实例方法

2、静态方法

3、实例方法中的代码块

4、静态方法中的代码块

这些块在不同的对象上是同步的。你需要哪种类型的同步块取决于具体的情况

同步实例方法

下面是一个同步实例方法

public synchronized void add(int value){

       this.count +=value;

}

注意在方法声明中使用synchronized关键字。这告诉Java该方法是同步的。

Java中的同步实例方法在拥有该方法的实例(对象)上同步。因此,每个实例的同步方法都在不同的对象上同步:拥有实例。只有一个线程可以在同步的实例方法中执行。如果存在多个实例,那么每次一个线程可以在每个实例的同步实例方法中执行。每个实例一个线程。

同步静态方法

静态方法被标记为同步,就像使用synchronized关键字的实例方法一样。下面是一个Java同步静态方法示例

public static synchronized void add(int value){

      count += value;

}

同样在这里,synchronized关键字告诉Java,该方法是同步的。

同步静态方法在类的类对象上同步。由于每个类只存在一个类对象,所以只有一个线程可以在同一个类中执行静态同步方法。

如果静态同步方法位于不同的类中,那么一个线程可以在每个类的静态同步方法中执行。

实例方法中的同步块

您不需要同步整个方法。有时最好只同步方法的一部分。在方法内部的Java同步块使这成为可能。

下面是一个非同步Java方法中Java代码的同步块:

public void add(int value){

      synchronized(this){

           this.count += value;

       }

}

这个例子使用Java同步块构造来标记一个代码块作为同步。这段代码现在执行起来就像一个同步的方法一样,请注意Java同步块构造如何在圆括号中使用对象。在这个例子中,使用了“this”,也就是调用add方法的实例。被同步构造的圆括号中的对象称为监视器对象。代码被认为是在监视器对象上同步的。同步的实例方法使用它所属的对象作为监视器对象,只有一个线程可以在同一个监视器对象上同步执行一个Java代码块,下面两个例子在它们被调用的实例上都是同步的。因此,它们在同步方面是等价的:

public class MyClass{

       public synchronized void log1(String msg1,String msg2){

            log.writeln(msg1);

            log.writeln(msg2);

         }

         public void log2(String msg1,String msg2){

                 synchronized(this){

                        log.writeln(msg1);

                        log.writeln(msg2);

                  }

           }

}


因此,只有一个线程可以在这个示例中的两个同步块中的任何一个中执行。如果第二个同步块在一个不同的对象上被同步,那么一次一个线程就能够在每个方法中执行。

静态方法中的同步块

下面是静态方法的两个例子。这些方法在类的类对象上是同步的:方法属于:

public class MyClass{

      public static synchronized void log1(String msg1,String msg2){

           log.writeln(msg1);

           log.writeln(msg2);

       }

       public static void log2(String msg1,String msg2){

             synchronized(this){

                  log.writeln(msg1);

                  log.writeln(msg2);

            }

      }

}

在这两种方法中,只有一个线程可以同时执行。如果第二个同步块在一个不同的对象上被同步,而不是MyClass类,然后一个线程可以同时在每个方法内执行。

Java同步的例子

这里有一个例子,它启动了两个线程,并让它们都在同一个计数器实例上调用add方法。一次只有一个线程能够在同一个实例上调用add方法,因为这个方法在它所属的实例上是同步的。

public class Counter{

       long count = 0;

       public synchronized void add(long value){

             this.count += value;

       }

}

public class CounterThread extends Thread{

         protected Counter counter = null;

         public CounterThread(Counter counter){

                 this.counter = counter;

         }

         public void run() {

               for(int i=0; i<10; i++){

                       counter.add(i);

                }

         }

}

public class Example {

        public static void main(String[] args){

                Counter counter = new Counter();

                ThreadthreadA = new CounterThread(counter);

                ThreadthreadB = new CounterThread(counter);

                 threadA.start();

                 threadB.start();

        }

}

创建两个线程。相同的计数器实例在它们的构造函数中传递给它们。add()方法在实例上是同步的,因为add方法是一个实例方法,并且标记为synchronized。因此,只有一个线程可以一次调用add()方法。另一个线程将等待第一个线程离开add()方法,然后才能执行该方法本身。

如果这两个线程引用了两个单独的计数器实例,那么就不会同时调用add()方法了。调用的对象应该是不同的对象,因此调用的方法也会在不同的对象上同步(拥有该方法的对象)。因此,通话不会阻塞。这就是它的样子:

public class Example {

          public static void main(String[] args){

                 Counter counterA = new Counter();

                 Counter counterB = new Counter();

                 ThreadthreadA = new CounterThread(counterA);

                 ThreadthreadB = new CounterThread(counterB);

                 threadA.start();

                 threadB.start();

        }

}

注意两个线程,threadA和threadB,不再引用相同的计数器实例。countA和countB的添加方法在它们的两个拥有实例上是同步的。调用add()在countA将不会阻塞在countB对add()的调用。

Java并发的效率

同步机制是Java的第一种用于同步对多个线程共享的对象的访问机制。尽管如此,同步机制并不十分先进。这就是为什么Java 5得了一套完整的并发工具类,以帮助开发人员实现更细粒度的并发控制,而不是同步的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,556评论 18 399
  • 1.解决信号量丢失和假唤醒 public class MyWaitNotify3{ MonitorObject m...
    Q罗阅读 867评论 0 1
  • 听说你要来, 我说“哦”。 装作漫不经心的样子。 把屋里悄悄环视了一遍, 快速整理好,一些凌乱的小细节。 走到镜子...
    花开两朵各表一枝阅读 378评论 5 7
  • 鱼油一天9粒 茶族6粒 B族12 VE9 VA3 蛋白粉6 果蔬9 倍立健3组。 护肝片一周一瓶
    袁文艺阅读 167评论 0 0
  • 《得到》得到,谁都想。得到,是每一个人的愿望,也是一种常规。不过,你想得到的,其实别人也想得到。得到,不一定是同类...
    cb9fa4682249阅读 157评论 0 1