sysynchronized關鍵字能夠修飾方法、代碼塊,但不能修飾構造器、成員變量等。java
當sysynchronized關鍵字同來修飾方法和代碼塊時,可以保證同一時刻最多隻有一個線程執行該段代碼或方法。防止當有兩個線程併發修改同一個文件時可能會形成異常。編程
同步代碼塊語法:安全
synchronized(obj){
...
//此處代碼是同步代碼塊
}
複製代碼
意思爲:當線程要執行如下代碼塊以前,必須先得到對obj的鎖。bash
然而,另外一個當一個線程訪問object的一個synchronized(this)代碼塊時,另外一個線程仍然能夠訪問該object中的非synchronized(this)同步代碼塊,可是其餘線程對object中其餘全部的synchronized(this)同步代碼塊的訪問將被阻塞。併發
示例:ide
public class Thread1 {
public void synchronize() {
synchronized (this) {
System.out.println("I am the synchronized part:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the synchronized part is over");
}
}
public void synchronize2() {
synchronized (this) {
System.out.println("I am the synchronized part 2:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the synchronized part 2 is over");
}
}
public void nonSynchronize() {
System.out.println("non-synchronized part:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the non-synchronized part is over");
}
public static void main(String[] args) {
final Thread1 thread1 = new Thread1();
//啓動線程,線程一、2和3分別用了不一樣的表達式
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
thread1.synchronize();
}
});
Thread t2 = new Thread(() -> thread1.synchronize2());
Thread t3 = new Thread(thread1::nonSynchronize);
t1.start();
t2.start();
t3.start();
}
}複製代碼
輸出結果:工具
從輸出結果能夠看出:線程0執行的是第一個同步代碼塊,線程2執行的是非同步代碼塊,線程0和2同時執行,二者互不干擾。線程1執行的是第二個同步代碼塊,由於線程0先得到對該對象的鎖定,因此線程1即便執行代碼塊跟線程0不同,也被阻塞,必須等線程1執行完同步代碼塊,釋放鎖,線程1才能繼續執行。this
public class Thread1 {
private synchronized void synchronize() {
System.out.println("I am the synchronized method part:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the synchronized method part is over");
}
private synchronized void synchronize2() {
System.out.println("I am the synchronized method part 2:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the synchronized method part 2 is over");
}
private void nonSynchronize() {
System.out.println("non-synchronized method part:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the non-synchronized method part is over");
}
public static void main(String[] args) {
final Thread1 thread1 = new Thread1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
thread1.synchronize();
}
});
Thread t2 = new Thread(() -> thread1.synchronize2());
Thread t3 = new Thread(thread1::nonSynchronize);
t1.start();
t2.start();
t3.start();
}
}複製代碼
輸出結果跟同步代碼塊同樣:spa
Lock 是控制多個線程對共享資源進行訪問的工具。每次只能有一個線程對Lock對象加鎖,線程開始訪問共享資源以前應先得到Lock對象。.net
在實現線程安全的控制中,比較經常使用的是ReentrantLock。
class X {
private final ReentrantLock lock = new ReentrantLock();
//定義須要保證線程安全的方法
public void m() {
//加鎖
lock.lock();
try {
//method body
} finally { //使用finally來保證釋放鎖
lock.unlock();
}
}
}複製代碼
參考資料:java加鎖與同步方法
瘋狂java講義(第三版)
Java編程思想(第4版)