七種情況
- 兩個線程同時訪問一個對象的同步方法
- 兩個線程訪問的是兩個對象的同步方法
- 兩個線程訪問的是synchronized修飾的靜態方法
- 同時訪問同步方法與非同步方法
- 訪問一個對象的不同的普通同步方法
- 同時訪問靜態synchronized方法和非靜態的synchronized方法
- 方法拋出異常後,會釋放鎖
一 | 兩個線程同時訪問一個對象的同步方法
- 兩個線程t1和t2
- 同一個對象,它們都是Runnabl的同一個實例instance
- 一個對象的同步方法,被synchronizd修飾的method
結論:串行
併發與串行其實是不同的概念,在單核CPU裏面,所有指令都是串行的,併發指的在某一個時間段內,多個線程執行。與串行相對應的是並行,之後用串行和並行來區別。
public class SynchronizedObjectMethod3 implements Runnable {
static SynchronizedObjectMethod3 instance = new SynchronizedObjectMethod3();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
method();
}
public synchronized void method() {
System.out.println("I'm object method lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm object method lock " + Thread.currentThread().getName() + " is over");
}
}
二 | 兩個線程訪問的是兩個對象的同步方法
- 兩個線程t1、t2
- 兩個對象instance1和instance2
儘管同步代碼塊對象鎖是this,但這個this在起作用是兩個不同的實例,用lock1也是一樣的。
結論:並行
public class SynchronizedObjectCodeBlock2 implements Runnable {
static SynchronizedObjectCodeBlock2 instance1 = new SynchronizedObjectCodeBlock2();
static SynchronizedObjectCodeBlock2 instance2 = new SynchronizedObjectCodeBlock2();
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
synchronized (this) {
System.out.println("I'm object code block lock1 " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm object code block lock1 " + Thread.currentThread().getName() + " is over");
}
// synchronized (lock2) {
// System.out.println("I'm object code block lock2 " + Thread.currentThread().getName());
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println("I'm object code block lock2 " + Thread.currentThread().getName() + " is over");
// }
}
}
三 | 兩個線程訪問的是synchronized修飾的靜態方法
public class SynchronizedStaticMethod4 implements Runnable {
static SynchronizedStaticMethod4 instance1 = new SynchronizedStaticMethod4();
static SynchronizedStaticMethod4 instance2 = new SynchronizedStaticMethod4();
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
method();
}
public static synchronized void method() {
System.out.println("I'm object method lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm object method lock " + Thread.currentThread().getName() + " is over");
}
}
結論:串行
四 | 同時訪問同步方法與非同步方法
結論當然是非同步方法不會受到影響
public class SynchronizedYesOrNo6 implements Runnable{
static SynchronizedYesOrNo6 instance = new SynchronizedYesOrNo6();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public synchronized void method1() {
System.out.println("I'm lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm lock " + Thread.currentThread().getName() + " is over");
}
public void method2() {
System.out.println("I'm not lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm not lock " + Thread.currentThread().getName() + " is over");
}
}
試想一下如果本該作爲臨界資源的變量i,除了在同步代碼塊內,還在普通方法的地方被修改,會發生什麼呢?
public class DisappearRequest1 implements Runnable {
static DisappearRequest1 instance = new DisappearRequest1();
static int i = 0;
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);
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public synchronized void method1() {
for(int j=0; j<100000; j++) {
i++;
}
}
public void method2() {
for(int j=0; j<100000; j++) {
i++;
}
}
}
i的最後結果是小於200000的…所以臨界資源必須進行互斥操作。
五 | 訪問一個對象的不同的普通同步方法
public class SynchronizedDifferentMethod7 implements Runnable{
static SynchronizedDifferentMethod7 instance = new SynchronizedDifferentMethod7();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public synchronized void method1() {
System.out.println("I'm lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm lock " + Thread.currentThread().getName() + " is over");
}
public synchronized void method2() {
System.out.println("I'm not lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm not lock " + Thread.currentThread().getName() + " is over");
}
}
結論串行:其實相當於是synchronized鎖了同一個this對象
六 | 同時訪問靜態synchronized方法和非靜態的synchronized方法
預測:並行
public class SynchronizedStaticAndNormal8 implements Runnable{
static SynchronizedStaticAndNormal8 instance = new SynchronizedStaticAndNormal8();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public static synchronized void method1() {
System.out.println("I'm static lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm static lock " + Thread.currentThread().getName() + " is over");
}
public synchronized void method2() {
System.out.println("I'm not static " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm not static " + Thread.currentThread().getName() + " is over");
}
}
結論:並行
原因:static加鎖的對象是該類的類類型,而普通方法鎖加鎖的對象是this,這顯然是兩把不同的鎖。
章七 | 方法拋出異常後,會釋放鎖
要使用RuntimeException來拋出異常
public class SynchronizedException9 implements Runnable {
static SynchronizedException9 instance = new SynchronizedException9();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public synchronized void method1() {
System.out.println("I'm static lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
// throw new Exception();
} catch (InterruptedException e) {
e.printStackTrace();
}
// catch (Exception e) {
// e.printStackTrace();
// }
throw new RuntimeException();
// System.out.println("I'm static lock " + Thread.currentThread().getName() + " is over");
}
public synchronized void method2() {
System.out.println("I'm not static " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm not static " + Thread.currentThread().getName() + " is over");
}
}
總結
- 一把鎖只能被一個線程所獲取,其他線程只能等待
- 每個實例都對應有自己的一把鎖,類鎖就是所有實例的類類型的鎖,有同一個類派生而來的實例自然只有一個類類型
- 無論發放還是正常執行完畢還是拋出異常,synchronized修飾的都將釋放鎖