一啊央、代碼示例
1.synchronized修飾普通方法
synchronized修飾普通代碼,加鎖對象為調(diào)用這個方法的對象
public class SynchronizedTest {
private int age;
public synchronized int getAge() throws InterruptedException {
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+"**"+System.currentTimeMillis());
return age;
}
public static void main(String []args){
SynchronizedTest synchronizedTest = new SynchronizedTest();
Thread thread1 = new Thread("t1"){
@Override
public void run(){
try {
synchronizedTest.getAge();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread2 = new Thread("t2"){
@Override
public void run(){
try {
synchronizedTest.getAge();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread1.start();
thread2.start();
}
}
因?yàn)?個線程調(diào)用getAge()方法的都是同一個對象synchronizedTest 幢码,所以2個線程是串行進(jìn)行,thread2會等待thread1
運(yùn)行結(jié)果如下
t1**1561547808386
t2**1561547818390
這里說一點(diǎn):最近在項(xiàng)目中洲脂,發(fā)現(xiàn)有人寫了這樣一段代碼
synchronized (jedisPool) {
if(jedisPool == null){
initialPool();
}
}
大家發(fā)現(xiàn)里面錯誤了么
這里強(qiáng)調(diào)一點(diǎn)竹勉,因?yàn)镴ava對象只有一個內(nèi)置鎖裹虫,synchronized 會在這個內(nèi)置鎖進(jìn)行加鎖肿嘲,我們平時定義對象為空類似
Object ob = null;
這個只是把對象指向一個空引用,并沒有任何初始化筑公,空在Java中是一種特殊類型雳窟,并不是對象。
如果代碼中判斷synchronized 加鎖對象為空應(yīng)該在synchronized 之前作出判斷,如果加鎖對象為空會報空指針異常封救。
這里我們再思考一個問題拇涤,如果多線程訪問同一對象的不同的被synchronized修飾的普通方法會發(fā)生什么
public class SynchronizedTest6 {
public synchronized void getAge() throws InterruptedException {
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+"**"+System.currentTimeMillis());
}
public synchronized void test() throws InterruptedException {
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+"**"+System.currentTimeMillis());
}
public static void main(String []args){
SynchronizedTest6 synchronizedTest6 = new SynchronizedTest6();
Thread thread1 = new Thread("t1"){
@Override
public void run(){
try {
synchronizedTest6.getAge();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread2 = new Thread("t2"){
@Override
public void run(){
try {
synchronizedTest6.test();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread1.start();
thread2.start();
}
}
因?yàn)閟ynchronized是加在對象內(nèi)置鎖上所以結(jié)果顯而易見,多線程會串行進(jìn)行,結(jié)果如下
t1**1561745488571
t2**1561745498572
2.synchronized修飾靜態(tài)方法
synchronized修飾靜態(tài)方法誉结,鎖會發(fā)生在這個靜態(tài)方法類上,多個線程訪問同一靜態(tài)方法會串行執(zhí)行
public class SynchronizedTest3 implements Runnable{
@Override
public void run() {
try {
test();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static synchronized void test() throws InterruptedException {
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+System.currentTimeMillis());
}
public static void main(String []args){
SynchronizedTest3 synchronizedTest3 = new SynchronizedTest3();
SynchronizedTest3 synchronizedTest31 = new SynchronizedTest3();
Thread thread1 = new Thread(synchronizedTest3);
Thread thread2 = new Thread(synchronizedTest31);
thread1.start();
thread2.start();
}
}
運(yùn)行結(jié)果如下
Thread-01561744252006
Thread-11561744262008
3.synchronized修飾類
效果類同synchronized修飾靜態(tài)方法
public class SynchronizedTest4 implements Runnable{
@Override
public void run() {
try {
test();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void test() throws InterruptedException {
synchronized (SynchronizedTest4.class){
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+System.currentTimeMillis());
}
}
public static void main(String []args){
SynchronizedTest4 synchronizedTest = new SynchronizedTest4();
SynchronizedTest4 synchronizedTest_ = new SynchronizedTest4();
Thread thread1 = new Thread(synchronizedTest);
Thread thread2 = new Thread(synchronizedTest_);
thread1.start();
thread2.start();
}
}
Thread-11561744702391
Thread-01561744712400
二鹅士、原理
在這里我們反編譯下class,先看下字節(jié)碼生成惩坑,這里推薦大家使用idea的插件jclasslib bytecode viewer查看字節(jié)碼
同步代碼塊其實(shí)是通過monitorenter和monitorexit指令實(shí)現(xiàn)的掉盅,我們前面說了每個對象都有一把內(nèi)置鎖,這個鎖其實(shí)是monitor(也被成為監(jiān)視器鎖)以舒,同一時間只能有一個對象獲取到monitor趾痘,monitorenter指令會嘗試獲取monitor,而monitorexit會釋放掉monitorexit蔓钟。