资讯专栏INFORMATION COLUMN

Java同步块简介

warnerwu / 3060人阅读

摘要:同步块用来避免竞争。实际需要那种同步块视具体情况而定。在非同步的方法中的同步块的例子如下所示示例使用同步块构造器来标记一块代码是同步的。在同步构造器中用括号括起来的对象叫做监视器对象。他们的构造器引用同一个实例。

Java 同步块(synchronized block)用来标记方法或者代码块是同步的。Java同步块用来避免竞争。本文介绍以下内容:

Java同步关键字(synchronzied)

实例方法同步

静态方法同步

实例方法中同步块

静态方法中同步块

Java同步示例

Java同步关键字(synchronized)

Java中的同步块用synchronized标记。同步块在Java中是同步在某个对象上。所有同步在一个对象上的同步块在同时只能被一个线程进入并执行操作。所有其他等待进入该同步块的线程将被阻塞,直到执行该同步块中的线程退出。

有四种不同的同步块:

实例方法

静态方法

实例方法中的同步块

静态方法中的同步块

上述同步块都同步在不同对象上。实际需要那种同步块视具体情况而定。

实例方法同步

下面是一个同步的实例方法:

 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);
       }
    }
  }

在上例中,每次只有一个线程能够在两个同步块中任意一个方法内执行。

如果第二个同步块不是同步在this实例对象上,那么两个方法可以被线程同时执行。

静态方法中的同步块

和上面类似,下面是两个静态方法同步的例子。这些方法同步在该方法所属的类对象上。

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(MyClass.class){
          log.writeln(msg1);
          log.writeln(msg2);
       }
    }
  }

这两个方法不允许同时被线程访问。

如果第二个同步块不是同步在MyClass.class这个对象上。那么这两个方法可以同时被线程访问。

Java****同步实例

在下面例子中,启动了两个线程,都调用Counter类同一个实例的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();
      Thread  threadA = new CounterThread(counter);
      Thread  threadB = new CounterThread(counter);

      threadA.start();
      threadB.start();
    }
  }

创建了两个线程。他们的构造器引用同一个Counter实例。Counter.add方法是同步在实例上,是因为add方法是实例方法并且被标记上synchronized关键字。因此每次只允许一个线程调用该方法。另外一个线程必须要等到第一个线程退出add()方法时,才能继续执行方法。

如果两个线程引用了两个不同的Counter实例,那么他们可以同时调用add()方法。这些方法调用了不同的对象,因此这些方法也就同步在不同的对象上。这些方法调用将不会被阻塞。如下面这个例子所示:

 public class Example {

    public static void main(String[] args){
      Counter counterA = new Counter();
      Counter counterB = new Counter();
      Thread  threadA = new CounterThread(counterA);
      Thread  threadB = new CounterThread(counterB);

      threadA.start();
      threadB.start();
    }
  }

注意这两个线程,threadA和threadB,不再引用同一个counter实例。CounterA和counterB的add方法同步在他们所属的对象上。调用counterA的add方法将不会阻塞调用counterB的add方法。

原文 Java Synchronized Blocks
作者 Jakob Jenkov
译者 李同杰
via ifeve

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64070.html

相关文章

  • JAVA线程间通信简介

    摘要:线程通信的目标是使线程间能够互相发送信号。但是,这个标志已经被第一个唤醒的线程清除了,所以其余醒来的线程将回到等待状态,直到下次信号到来。如果方法调用,而非,所有等待线程都会被唤醒并依次检查信号值。 线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。 showImg(http://segmentfault.com/img/bVbPLD); 例...

    CHENGKANG 评论0 收藏0
  • Java重入锁死简介

    摘要:可重入的意思是线程可以重复获得它已经持有的锁。如果一个线程持有某个管程对象上的锁,那么它就有权访问所有在该管程对象上同步的块。下面这个锁的实现是不可重入的如果一个线程在两次调用间没有调用方法,那么第二次调用就会被阻塞,这就出现了重入锁死。 重入锁死与死锁和嵌套管程锁死非常相似。锁和读写锁两篇文章中都有涉及到重入锁死的问题。 showImg(http://segmentfault.co...

    LeexMuller 评论0 收藏0
  • Java锁机制

    摘要:它俩是不冲突的,也就是说获取了类锁的线程和获取了对象锁的线程是不冲突的可重入锁住了锁住了当线程进入到的方法时,此时拿到了实例对象的锁。当一个线程执行的代码出现异常时,其所持有的锁会自动释放。 Java锁机制 synchronized锁 synchronized 简介 synchronized是Java的一个关键字,它能够将代码块(方法)锁起来 synchronized是一种互...

    FreeZinG 评论0 收藏0
  • java并发编程学习1--基础知识

    摘要:死亡状态线程退出有可能是正常执行完成也有可能遇见异常退出。类有新建与死亡状态返回其余状态返回判断线程是否存活。线程因某些原因进入阻塞状态。执行同步代码块的过程中执行了当前线程放弃开始睡眠进入就绪状态但是不会释放锁。 【java内存模型简介 JVM中存在一个主存区(Main Memory或Java Heap Memory),Java中所有变量都是存在主存中的,对于所有线程进行共享,而每个...

    huangjinnan 评论0 收藏0
  • 高并发 - 收藏集 - 掘金

    摘要:在中一般来说通过来创建所需要的线程池,如高并发原理初探后端掘金阅前热身为了更加形象的说明同步异步阻塞非阻塞,我们以小明去买奶茶为例。 AbstractQueuedSynchronizer 超详细原理解析 - 后端 - 掘金今天我们来研究学习一下AbstractQueuedSynchronizer类的相关原理,java.util.concurrent包中很多类都依赖于这个类所提供的队列式...

    levius 评论0 收藏0

发表评论

0条评论

warnerwu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<