後臺的同事在開發隨機生成的試題,開發考試這塊的功能,從用戶登錄到生成隨機試卷,再到顯示到過程中,併發量在到100左右的時候就比較的卡。想想幾年前的java線程方面的知識,感覺自己已經忘的差不多了,今天比較空閒,就總結一下java的synchronized關鍵字,純屬自己的理解,如果有不對的地方還請大家指出,共同進步!
synchronized 是Java語言的關鍵字,當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多隻有一個線程執行該段代碼。
一、當兩個併發線程訪問**同一個對象object中**的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。
二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊**(注意的是同一個object)**。
三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞**(注意的是同一個object)**。
四、第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對**該object對象**所有同步代碼部分的訪問都被暫時阻塞。
舉例說明:
package com.tian.threadtestdemo.utils;
/**
*
* @author TCX
*
*/
public class SameObjectTest {
//同步代碼塊
public void text1(){
//this表示添加的對象鎖(this具體指的是什麼呢?它指的就是調用這個方法的對象,如P1。)
synchronized (this) {
for(int i=0;i<5;i++){
System.out.println("text1--->"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//非同步方法體或代碼塊
public void text2(){
for(int i=0;i<5;i++){
System.out.println("text2--->"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//同步方法體
public synchronized void text3(){
for(int i=0;i<5;i++){
System.out.println("text3--->"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void text4(){
**String str=new String();**
synchronized (str) {
for(int i=0;i<5;i++){
System.out.println("text3--->"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
測試代碼如下:
package com.tian.threadtestdemo;
import com.tian.threadtestdemo.utils.SameObjectTest;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
public class SameActivity extends Activity implements OnClickListener {
private SameObjectTest text1;
private SameObjectTest text2;
private SameObjectTest text3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_same);
initView();
}
/**
* 初始化頁面控件
*/
private void initView() {
View btn1 = findViewById(R.id.btn_1);
View btn2 = findViewById(R.id.btn_2);
View btn3 = findViewById(R.id.btn_3);
View btn4 = findViewById(R.id.btn_4);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
btn4.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_1:
//組合一
startThread1();
break;
case R.id.btn_2:
//組合二
startThread2();
break;
case R.id.btn_3:
//組合三
startThread3();
break;
case R.id.btn_4:
//組合四
startThread4();
break;
default:
break;
}
}
/**
* 啓動組合一
*/
private void startThread1() {
text1 = new SameObjectTest();
new Thread(new Runnable() {
@Override
public void run() {
text1.text1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
text1.text2();
}
}).start();
}
/**
* 啓動組合二
*/
private void startThread2() {
text2 = new SameObjectTest();
new Thread(new Runnable() {
@Override
public void run() {
text2.text1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
text2.text3();
}
}).start();
}
/**
* 啓動組合三
*/
private void startThread3() {
text3 = new SameObjectTest();
new Thread(new Runnable() {
@Override
public void run() {
text3.text2();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
text3.text3();
}
}).start();
}
/**
* 啓動組合四
*/
private void startThread4() {
text4 = new SameObjectTest();
new Thread(new Runnable() {
@Override
public void run() {
text4.text1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
text4.text4();
}
}).start();
}
}
組合一的打印結果爲:
可以看的出來,打印結果是交替出現,說明了在幾乎同時啓動子線程去調用同對象中,同步代碼塊和非同步代碼塊的同時,同步代碼塊並沒有阻塞非同步代碼塊的運行
組合二的打印結果爲:
可以看的出來,打印結果不是交替出現,說明了在幾乎同時啓動子線程去調用同對象中,添加相同的鎖的代碼塊,在執行過程中發生了阻塞,在text1()方法體執行完之後,纔對text3()執行。
組合三的打印結果爲:
結果跟組合一的打印結果一致。不多解釋了。。。
組合四的打印結果爲:
注意的是text4()方法體中的鎖對象變化了哦!
String str=new String();
synchronized (str){
//、、、
}
對象鎖發生了變化,當然不同步咯!哈哈哈!
synchronized的拓展
1.假設P1、P2是同一個類的不同對象,這個類中定義了以下幾種情況的同步塊或同步方法,P1、P2就都可以調用它們。這也就是同步方法,那這時synchronized鎖定的是哪個對象呢?它鎖定的是調用這個同步方法對象。也就是說,當一個對象P1在不同的線程中執行這個同步方法時,它們之間會形成互斥,達到同步的效果。但是這個對象所屬的Class所產生的另一對象P2卻可以任意調用這個被加synchronized關鍵字的方法。
2.如果一個類中定義了一個synchronized的static函數A,也定義了一個synchronized 的instance函數B,那麼這個類的同一對象Obj在多線程中分別訪問A和B兩個方法時,不會構成同步,因爲它們的鎖都不一樣。A方法的鎖是Obj這個對象,而B的鎖是Obj所屬的那個Class,當然也不會產生同步的效果。
當然,使用同步鎖可以提高我們代碼的準確性,但是並不是所有的情況都適用。適用同步鎖對程序的開銷是非常大的,搞不好還會出現死鎖的情況,所以呢,還是慎用哦!