Java多线程中的同步机制会对资源进行加锁,保证同一时刻只有一个线程操作该资源,避免多个线程同时访问该资源造成冲突。
Synchronized是Java中的同步锁关键字,主要修饰的对象有:
-
修饰非静态方法
同步的是外部调用的实例对象,如new Sync()。多线程中不同实例调用a(),同步的是不同的Sync实例,同步无效。 -
修饰静态方法
同步的是包含此方法的类,如Sync.class。多线程中不同实例调用b(),同步的是同一个Sync.class,同步有效。 -
修饰方法块
同步的对象是synchronized(obj)内的非NULL的Object。- 若obj为Sync.class,则同步Sync.class本身,同理synchronized修饰静态方法;
- 若obj为类属性,如string变量,则同步的是变量所属对象实例,即是外部调用的对象实例。
修饰非静态方法示例:
/**
* a()方法是经过synchronized修饰的非静态方法,同步的是在外部调用的对象实例。
* 由于外部多线程中是不同的对象实例,则对count的操作未实现同步(示例现象:未有序递减)。
*/
public synchronized void a() {
print();
}
修饰静态方法示例:
/**
* b()方法是经过synchronized修饰的静态方法,同步的是本类的对象实例,和外部多线程中的调用对象实例无关。
* 外部多线程,谁先获得本类的对象锁,其他线程则处于阻塞状态,直到获得锁的线程解锁。
*/
public static synchronized void b() {
print();
}
修饰方法块示例:
/**
* c()和d()是静态方法和非静态方法的壳,内部synchronized修饰的是方法块。
* 同步的对象是synchronized(obj)内的非NULL的Object。
* 1.若obj为Sync.class,则同步Sync.class本身,同理synchronized修饰静态方法;
* 2.若obj为类属性,如string变量,则同步的是变量所属对象实例,即是外部调用的对象实例。
*/
public void c() {
synchronized (string) {
print();
}
}
public static void d() {
synchronized (Sync.class) {
print();
}
}
外部多线程中不同对象实例的调用:
Count.INSTANT.count = 100;
new Thread(() -> new Sync().a()).start();
new Thread(() -> new Sync().a()).start();
print打印操作如下:
public static void print() {
while (Count.INSTANT.count > 0) {
Count.INSTANT.count = Count.INSTANT.count - 1;
Log.d("Sync", "count = " + Count.INSTANT.count);
}
}
单一数据源如下:
/**
* 序列化单例模式,确保操作的是单一数据源
*/
public enum Count {
INSTANT;
public int count;
}
补充知识点:
- 类的静态成员变量和方法,是存储在方法区的,使用时均不用创建对象,在类加载初始化后即可使用。
- 类的非静态成员变量在类的实例化时分配内存并初始化,届时才可调用。非静态成员的值存放在堆区,其中基本数据类型直接保存值,复杂类型保存指向堆对象的引用。