Java Synchronization

本文深入探讨了Java并发编程中的核心概念,包括volatile变量的作用及其限制、不可变对象的设计原则及其实现方式、同步方法与块的区别以及原子操作的特点。通过具体示例,帮助读者理解如何正确地使用这些特性来实现线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Volatile

Since Java 5 the volatile keyword guarantees more than just the reading from and writing to main memory of variables. Actually, the volatile keyword guarantees this:

  • If Thread A writes to a volatile variable and Thread B subsequently reads the same volatile variable, then all variables visible to Thread A before writing the volatile variable, will also be visible to Thread B after it has read the volatile variable.

  • The reading and writing instructions of volatile variables cannot be reordered by the JVM (the JVM may reorder instructions for performance reasons as long as the JVM detects no change in program behaviour from the reordering). Instructions before and after can be reordered, but the volatile read or write cannot be mixed with these instructions. Whatever instructions follow a read or write of a volatile variable are guaranteed to happen after the read or write.

Volatile is not enough to guarantee the thread safety as it cannot protect the non-atomic operations, like counter++

If two threads are both reading and writing to a shared variable, then using the volatile keyword for that is not enough. You need to use a synchronized in that case to guarantee that the reading and writing of the variable is atomic. Reading or writing a volatile variable does not block threads reading or writing.

In case only one thread reads and writes the value of a volatile variable and other threads only read the variable, then the reading threads are guaranteed to see the latest value written to the volatile variable. Without making the variable volatile, this would not be guaranteed.

Immutable Objects

final fields, which cannot be modified after the object is constructed, can be safely read through non-synchronized methods, once the object is constructed

Rules specified in https://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html

  1. Don't provide "setter" methods — methods that modify fields or objects referred to by fields.
  2. Make all fields final and private.
  3. Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.
  4. If the instance fields include references to mutable objects, don't allow those objects to be changed:
    • Don't provide methods that modify the mutable objects.
    • Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.

An example of Immutable Obejct

final public class ImmutableRGB {

    // Values must be between 0 and 255.
    final private int red;
    final private int green;
    final private int blue;
    final private String name;

    private void check(int red,
                       int green,
                       int blue) {
        if (red < 0 || red > 255
            || green < 0 || green > 255
            || blue < 0 || blue > 255) {
            throw new IllegalArgumentException();
        }
    }

    public ImmutableRGB(int red,
                        int green,
                        int blue,
                        String name) {
        check(red, green, blue);
        this.red = red;
        this.green = green;
        this.blue = blue;
        this.name = name;
    }


    public int getRGB() {
        return ((red << 16) | (green << 8) | blue);
    }

    public String getName() {
        return name;
    }

    public ImmutableRGB invert() {
        return new ImmutableRGB(255 - red,
                       255 - green,
                       255 - blue,
                       "Inverse of " + name);
    }
}

Synchronized

Synchronized Method

public class SynchronizedCounter {
   private int c = 0;

   public synchronized void increment() {
       c++;
   }

   public synchronized void decrement() {
       c--;
   }

   public synchronized int value() {
       return c;
   }
}

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads. 

Synchronized on methods would block the whole object, like synchronized (this) { }.

if the setValue() method in a class is synchronized but getValue() method is not, then while a thread enters the setValue() method block, other threads could also enter getValue() method block at the same time. If getValue() method is also synchronized, then only one thread could enter one of the two methods at the same time. Test codes are:

public class TestMethod {
    public static void main(String[] args) {
        final TestObject testObject = new TestObject(0);

        new Thread() {
            @Override
            public void run() {
                testObject.setValue(1);
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                testObject.getValue();
                System.out.println("got value");
            }
        }.start();

    }
}

class TestObject {
    int value;

    public TestObject(int value) {
        this.value = value;
    }

    synchronized void setValue(int value) {
        System.out.println("Entering the setvalue lock, waiting forever " + Thread.currentThread().getName());
        this.value = value;
        while (true) {

        }
    }

    int getValue() {
        return value;
    }
}

Synchronized Block

Blocks do have advantages over methods, most of all in flexibility because you can use other object as lock whereas syncing the method would lock the complete class. This is usually the safest way to do the sunchronization.

private Object lock = new Object();

private void someInputRelatedWork() {

   synchronize(lock) { ...

   }

}

Synchronized class

synchronized (MyClass.class) { } means lock on all the objects instantiated from the MyClass.class.


Synchronized Static Method

Since a static method has no associated object, the synchronized keyword lock on the class instead of the object?


The worst solution is to put the "synchronized" keywords on the static methods, which means it will lock on all instances of this class.


One point you have to be careful about (several programmers generally fall in that trap) is that there is no link between synchronized static methods and sync'ed non static methods, ie:

class A {

     static synchronized f() {...}

     synchronized g() {...} #

}


Main:

A a = new A();


Thread 1:

A.f();


Thread 2:

a.g();


f() and g() are not synchronized with each other and thus can execute totally concurrently.


You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class's static fields is controlled by a lock that's distinct from the lock for any instance of the class.

Atomic Access

An atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side effects of an atomic action are visible until the action is complete.

  • Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
  • Reads and writes are atomic for all variables declared volatile (including long and double variables).



转载于:https://www.cnblogs.com/codingforum/p/6608029.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值