首先先看看下面的代碼:安全
public class checkSynchronized extends Thread{
static volatile int i = 0;
public static void increase() {
i++;
}
@Override
public void run() {
for (int j = 0; j < 1000000; j++) {
increase();
}
}
public static void main(String args[]) throws InterruptedException {
Thread t1 = new checkSynchronized();
Thread t2 = new checkSynchronized();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("i : " + i);
}
}
複製代碼
執行上述代碼,你會發現大部分狀況下i的最終值都會小於2000000的bash
爲何加了volatile關鍵詞的變量依舊會出現線程安全問題呢?多線程
這是由於volatile只保證可見性,不保證原子性ide
關鍵字synchronized的做用就是實現線程間的同步問題,它能將同步區的代碼進行加鎖,一次只能容許一條線程進入同步區,以保證同步區中的線程安全問題。 下面就來測試下該關鍵字的做用:性能
public class checkSynchronized extends Thread{
Object lock;
checkSynchronized(Object lock) {
this.lock = lock;
}
static volatile int i = 0;
public static void increase() {
i++;
}
@Override
public void run() {
synchronized (lock) {
for (int j = 0; j < 1000000; j++) {
increase();
}
}
}
public static void main(String args[]) throws InterruptedException {
Object lock = new Object();
Thread t1 = new checkSynchronized(lock);
Thread t2 = new checkSynchronized(lock);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("i : " + i);
}
}
複製代碼
你們能夠看到在run()方法里加了synchronized,而且指定了鎖對象。測試
運行結果:i : 2000000
this
public static void main(String args[]) throws InterruptedException {
Object lock1 = new Object(); //生成第一個鎖
Object lock2 = new Object(); //生成第二個鎖
Thread t1 = new checkSynchronized(lock1);
Thread t2 = new checkSynchronized(lock2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("i : " + i);
}
複製代碼
這段代碼表示出各自線程最終使用了各自的鎖,線程安全是沒法保證的。spa
public class checkSynchronized implements Runnable{
static volatile int i = 0;
public synchronized void increase() {
i++;
}
@Override
public void run() {
for (int j = 0; j < 1000000; j++) {
increase();
}
}
public static void main(String args[]) throws InterruptedException {
checkSynchronized onlyOne = new checkSynchronized();
Thread t1 = new Thread(onlyOne);
Thread t2 = new Thread(onlyOne);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("i : " + i);
}
}
複製代碼
上述代碼的synchronized做用在實例方法上,鎖爲當前實例。因此在main方法中只實例了一個checkSynchronized實例,由於鎖只須要一個、多了就會出現安全問題。線程
若是實例了兩個對象,則會出現安全問題(鎖多了和不加鎖就沒啥區別了),以下述代碼:code
public static void main(String args[]) throws InterruptedException {
checkSynchronized onlyOne = new checkSynchronized();
checkSynchronized wrong = new checkSynchronized();
Thread t1 = new Thread(onlyOne);
Thread t2 = new Thread(wrong);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("i : " + i);
}
複製代碼
public static synchronized void increase()
這樣鎖就做用在類方法上了。當線程要執行該同步方法時是請求當前類的鎖並不是實例的鎖,因此再多的實例線程之間依舊能正確同步。
synchronized不只用於線程同步、確保線程安全問題外,還能保證線程之間的可見性和有序性問題。至關因而volatile的升級版。但被synchronized限制的多線程之間是串行執行的,所帶來的性能消耗是很大的。