Java同步块(关键字: synchronized)

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

●Java同步关键字(synchronized)

●实例方法同步

●静态方法同步

●实例方法中同步

●静态方法中同步

●Java同步实例

Java同步关键字(synchronized)

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

有四种不同的同步块:

1. 实例方法

2. 静态方法

3. 实例方法中的同步块

4. 静态方法中的同步块

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

实例同步方法

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

public synchronized void add(int value){
 this.count += value;
}

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

Java实例方法同步是同步在拥有该方法的对象上。这样, 每个实例其方法同步都同步在不同对象上, 即改方法所属的实例。只有一个线程能够在实例方法同步块中运行。如果有多个实例存在, 那么一个线程一个可以在一个实例同步块中执行操作。一个实例一个进程。

静态方法同步

静态方法同步和实例方法同步一样, 也使用 synchronized关键字。Java静态方法同步方法如下实例:

public static sysynchronized void add(int value){
    this.count += value;
}

同样, 这里synchronized关键字告诉我们这个方法是同步的。

静态方法的同步是指同步在该方法所在的类对象上。因为在Java虚拟机中一个类只能对应一个对象, 所以同时只允许一个线程执行同一个类的静态同步方法。

对于不同类中的静态同步方法, 一个线程可以执行每个类中的静态同步方法而无需等待。不管类中的那个静态同步方法被调用, 一个类只能由一个线程同时执行。

实例方法中的同步块

又是你不需要同步整个方法, 而是同步方法中的一部风。Java可以对方法中的一部分进行同步。

在非同步的Java方法中的同步块的列子如下所示:

public void add(int value){
    ssynchronized(this){
        this.count += value;
    }
}

示例中使用Java同步块构造器来标记代码是同步的。改代码在执行时和同步方法一样。

注意Java同步快构造器用括号将对象括起来。在上例中, 使用了"this", 即为调用add方法的实例本身。在同步构造器中用括号括起来的对象叫做监视器对象。上述代码使用监视器对象同步, 同步实例方法使用调用方法本身作为的实例作为监视对象。

一次只有一个线程能够在同步于同一个监视对象的Java方法执行。

下面连个例子都同步他们所调用的实例对象上, 因此他们在同步的执行效果是相等的。

public class Main{

    public synchronized void log1(String msg1, String msg2){
        log.write(msg1);
        log.write(msg2);
    }    
    
    public void log2(Strubg msg1, String msg2){
        synchronized(this){
            log.write(msg1);
            log.write(msg2);
        }
    }

}

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

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

静态方法中的同步块

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

public class Main{

    public static synchronized void log1(String msg1, String msg2){
        log.write(msg1);
        log.write(msg2);
    }    
    
    public static void log2(Strubg msg1, String msg2){
        synchronized(this){
            log.write(msg1);
            log.write(msg2);
        }
    }

}

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

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

Java同步实例

在下面例子中, 启动了连个线程, 都调用Counter类同一个实例都add方法。因为同步在该方法所属的实例上, 所以同时只能有一个线程访问该方法。

public class Counter{

    long count = 0;
    
    public synchronized void add(int value){
        this.count += value;
    }

}
public class CountThread extend Thread{

    protected Counter counter = null;
    
    public CounterThread(Counter counter){
        this.counter = counter;
    }
    
    public void run(){
        for(int i=1; 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方法是实例方法并且标记上synchronize的关键字。因此每次只允许一个线程调用该方法。另外一个线程必须要等待第一个线程退出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()方法。

博客来源: http://ifeve.com/synchronized-blocks/

猜你喜欢

转载自blog.csdn.net/love_moon821/article/details/83269802