在 Java 中,关键字 synchronized 可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作) synchronized 的另外一个重要的作用,synchronized 可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到
The Java programming language provides two basic synchronization idioms:
synchronized methods and synchronized statements.
we are talking about synchronization
idioms
synchronization
idioms
To make a method synchronized, simply add the synchronized
keyword to its declaration:
public class SynchronizedCounter {
private int c = 0;
public synchronized void increment() {
c++;
}
public synchronized void decrement() {
c--;
}
public synchronized int value() {
return c;
}
}
making these methods synchronized has two effects
- methods on the same object to interleave is impossible, other thread must blocked until executing thread finish
- when a synchronized method exits, Object lock auto release(we will talk later), any subsequent invocation of a synchronized method for the same object must get lock before execute which automatically establishes a happens-before relationship. This guarantees that changes to the state of the object are visible to all threads.
Note: constructors cannot be synchronized, That’s doesn’t make sense, only thread creates an Object have access to it while it is being constructed
Note: When constructing an object that will be shared between threads, be very careful that a reference to the object does not “leak” prematurely.
if we maintain an List in one thread. which hold all instances, the constructor will look like
//...new List
instances.add(this);
If you do that, other thread can call the instances (this) before the construction finish. Which will cause problem.
all reads or writes to that object’s variables are done through synchronized
methods with one exception final
field, which can not modify after constructed, can be safely read through non-synchronized methods
Intrinsic Locks
we already know sunchronization
is to avoid mem-consist problem , How does that work?
Synchronization is built around an internal entity known as the intrinsic lock or monitor lock (an entity simply as a “monitor.”) with two job
- enforcing exclusive access to an object’s state
- establishing happens-before relationships that are essential to visibility.
Every object has an intrinsic lock associated with it. when one thread need exclusive and consistent access to an Object, have to acquire lock and release when finish There is no chance to get the same lock when other thread own that. The other thread will block when it attempts to acquire the lock.
The lock release occurs even if the return was caused by an uncaught exception.
we also have static sync method, which is associated with a class, not an object.
public class MyClass {
private static int staticField = 0; // 静态字段
private int instanceField = 0; // 实例字段
// 静态方法,访问静态字段
public static synchronized void incrementStaticField() {
staticField++;
}
// 实例方法,访问实例字段
public synchronized void incrementInstanceField() {
instanceField++;
}
}
the thread acquires the intrinsic lock for the Class
object associated with the class, which is different from any instance lock.
Synchronized Statements
Another way to create synchronized code is with synchronized statements. Unlike synchronized methods, synchronized statements must specify the object that provides the intrinsic lock:
public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
nameList.add(name);
}
In that case, addName
has to sync change with lastName and nameCount, but also needs to avoid synchronizing invocations of other objects’ methods. (this may cause a deadlock, will talk later)
Without synchronized statements, there would have to be a separate, unsynchronized method for the sole purpose of invoking nameList.add
.
Synchronized statements are also useful for improving concurrency with fine-grained synchronization.
for example, we have two field c1 and c2, they never used together and we should keep all field sync, there’s no reason to prevent an update of c1 from being interleaved with an update of c2, since they wont rely on others.
Instead of using synchronized methods or otherwise using the lock associated with this
, we create two objects solely to provide locks.
public class MsLunch {
private long c1 = 0;
private long c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void inc1() {
synchronized(lock1) {
c1++;
}
}
public void inc2() {
synchronized(lock2) {
c2++;
}
}
}
In that case, two variable can update interleaved.
these two objects considered “locks” not two instences
Reentrant Synchronization
Recall that a thread cannot acquire a lock owned by another thread. But a thread can acquire a lock that it already owns.
Allowing a thread to acquire the same lock more than once enables reentrant synchronization. This describes a situation where synchronized code, directly or indirectly, invokes a method that also contains synchronized code, and both sets of code use the same lock.
Without reentrant synchronization, synchronized code would have to take many additional precautions to avoid having a thread cause itself to block. (dead lock we will talk later)
next atomic
synchronized 关键字最主要有以下 3 种应用方式:
-
同步方法,为当前对象加锁,进入同步代码前要获得当前对象的锁;
-
同步静态方法,为当前类加锁,进入同步代码前要获得当前类的锁;
public class AccountingSyncClass implements Runnable {
static int i = 0;
/**
* 同步静态方法,锁是当前class对象,也就是
* AccountingSyncClass类对应的class对象
*/
public static synchronized void increase() {
i++;
}
// 非静态,访问时锁不一样不会发生互斥
public synchronized void increase4Obj() {
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
//new新实例
Thread t1=new Thread(new AccountingSyncClass());
//new新实例
Thread t2=new Thread(new AccountingSyncClass());
//启动线程
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}
/**
* 输出结果:
* 2000000
*/
- 同步代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
public class AccountingSync2 implements Runnable {
static AccountingSync2 instance = new AccountingSync2(); // 饿汉单例模式
static int i=0;
@Override
public void run() {
//省略其他耗时操作....
//使用同步代码块对变量i进行同步操作,锁对象为instance
synchronized(instance){
for(int j=0;j<1000000;j++){
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}
这里的锁指的是 Java 内置的隐式锁 monitor 也是
synchronized
封装好的实现 每个对象都有一个对象锁,不同的对象,他们的锁不会互相影响。
synchronized 与 happens before
[[JMM内存模型]]