前言
为了控制多个线程对共享资源(内存、文件、数据库等)的并发访问,避免访问冲突,使得共享资源被安全有序的进行访问,引入了线程“同步”机制。
synchronized原理
java中每个对象有且仅有一个同步锁。不同线程对同步锁的访问是互斥的,线程通过synchronized关键字获得同步锁,从而实现线程同步。也就是说,任何时候对象的同步锁只能被一个线程持有,通过持有同步锁实现对共享资源的互斥访问。
synchronized基本原则
- 当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程对该对象的synchronized方法或代码块的访问都被阻塞。
- 当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程可以访问该对象的非同步方法及非同步代码块。
- 当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程对该对象的其他synchronized方法或代码块的访问都被阻塞。
以上原则都在进一步强调说明,多个线程只有获取同一个对象的同步锁,才能实现互斥访问。否则可并发访问。
原则一、原则三
一、当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程对该对象的synchronized方法或代码块的访问都被阻塞。三、当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程对该对象的其他synchronized方法或代码块的访问都被阻塞。示例代码:
public class SyncDemo {
public synchronized void instanceSyncA() {
print("instanceSyncA");
}
public synchronized void instanceSyncB() {
print("instanceSyncB");
}
public void instanceSyncBlock() {
synchronized (this) {
print("instanceSyncBlock");
}
}
public static synchronized void staticSyncA() {
print("staticSyncA");
}
public static synchronized void staticSyncB() {
print("staticSyncB");
}
public void staticSyncBlock() {
synchronized (SyncDemo.class) {
print("staticSyncBlock");
}
}
public void noSync() {
print("noSync");
}
private static void print(String calledName) {
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName() + " : " + calledName);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
以下示例代码为获取SyncDemo实例对象的同步锁:
public class SyncTest {
/**
* 线程互斥访问同一个实例同步锁
*/
@Test
public void instanceSync() {
SyncDemo syncDemo = new SyncDemo();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.instanceSyncA();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.instanceSyncA();
}
});
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.instanceSyncB();
}
});
Thread thread4 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.instanceSyncBlock();
}
});
thread1.start();
thread2.start();
thread3.start();
thread4.start();
//以下代码为避免主线程退出,而导致测试提前结束
try {
thread1.join();
thread2.join();
thread3.join();
thread4.join();
} catch (InterruptedException e) {
}
}
}
运行结果:
Thread-0 : instanceSyncA
Thread-0 : instanceSyncA
Thread-3 : instanceSyncBlock
Thread-3 : instanceSyncBlock
Thread-2 : instanceSyncB
Thread-2 : instanceSyncB
Thread-1 : instanceSyncA
Thread-1 : instanceSyncA
结果说明这四个线程是互斥访问SyncDemo实例同步锁的,public synchronized void instanceSyncA()、public synchronized void instanceSyncB()这两个同步方法及synchronized (this)同步代码块获取的是同一个同步锁。线程thread1调用instanceSyncA取得同步锁,导致thread2调用instanceSyncA阻塞,满足原则一;同样,线程thread1调用instanceSyncA取得同步锁导致线程thread3、线程thread4阻塞,满足原则三。
原则二
当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程可以访问该对象的非同步方法及非同步代码块。示例代码:
public class SyncTest {
/**
* 其他线程仍可访问非同步代码
*/
@Test
public void syncAndnone() {
SyncDemo syncDemo = new SyncDemo();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.instanceSyncA();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.noSync();
}
});
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.noSync();
}
});
thread1.start();
thread2.start();
thread3.start();
try {
thread1.join();
thread2.join();
thread3.join();
} catch (InterruptedException e) {
}
}
}
运行结果:
Thread-1 : noSync
Thread-2 : noSync
Thread-0 : instanceSyncA
Thread-1 : noSync
Thread-0 : instanceSyncA
Thread-2 : noSync
结果说明这三个线程没有获取同一个同步锁,是可以并发执行的。thread1调用instanceSyncA取得同步锁,由于noSync并不是同步方法,因此并没有造成线程thread2和线程thread3的阻塞。
实例锁和全局锁
文章开头已经强调,多个线程只有获取同一个同步锁,才能进行互斥访问
实例锁是指锁在对象的实例上,一个对象有多个实例,也就意味着有多个同步锁,锁有效范围是同一个实例。不同线程分别获取不同实例上的锁是不能实现互斥访问的。
全局锁是指锁在类对象上或是static关键字修饰的类变量上,锁有效范围是所有对象实例及类方法,全局锁用static synchronized修饰方法或是synchronized(类对象/类变量)同步代码块表示。代码示例:
public class SyncTest {
/**
* 全局锁
*/
@Test
public void staticSync() {
SyncDemo syncDemo = new SyncDemo();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
SyncDemo.staticSyncA();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.staticSyncB();
}
});
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.staticSyncBlock();
}
});
Thread thread4 = new Thread(new Runnable() {
@Override
public void run() {
syncDemo.instanceSyncA();
}
});
thread1.start();
thread2.start();
thread3.start();
thread4.start();
try {
thread1.join();
thread2.join();
thread3.join();
thread4.join();
} catch (InterruptedException e) {
}
}
}
运行结果
Thread-0 : staticSyncA
Thread-3 : instanceSyncA
Thread-3 : instanceSyncA
Thread-0 : staticSyncA
Thread-2 : staticSyncBlock
Thread-2 : staticSyncBlock
Thread-1 : staticSyncB
Thread-1 : staticSyncB
线程thread1、thread2和thread3运行结果说明全局锁对类方法及实例都生效。线程thread4与线程thread1并发执行,说明获取的不是同一个锁,线程thread1获取的是全局锁,导致线程thread2和thread3阻塞;而thread4获取的是实例锁。
网友评论