synchronized同步語句塊
用關(guān)鍵字synchronized聲明方法是有弊端的小泉。比如線程A調(diào)用同步方法執(zhí)行一個長時間任務(wù)瞭空,那么線程B就要等較長時間才能調(diào)用冕屯。
下面看一個例子:
public class Task {
private String getData1;
private String getData2;
public synchronized void longTimeTask(){
try {
System.out.println("begin task");
Thread.sleep(3000);
getData1 = "長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=" + Thread.currentThread().getName();
getData2 = "長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=" + Thread.currentThread().getName();
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class Utils {
public static long begainTime1;
public static long endTime1;
public static long begainTime2;
public static long endTime2;
}
public class MyThread extends Thread{
private Task task;
private String name;
public MyThread(Task task, String name){
super();
this.task=task;
this.name=name;
super.setName(name);
}
@Override
public void run() {
super.run();
if ("A".equals(name)){
Utils.begainTime1 = System.currentTimeMillis();
task.longTimeTask();
Utils.endTime1 = System.currentTimeMillis();
}else {
Utils.begainTime2 = System.currentTimeMillis();
task.longTimeTask();
Utils.endTime2 = System.currentTimeMillis();
}
}
}
public class Main {
public static void main(String[] args){
Task task = new Task();
MyThread myThread = new MyThread(task, "A");
MyThread myThread1 = new MyThread(task, "B");
myThread.start();
myThread1.start();
try {
Thread.sleep(10000);
}catch (InterruptedException e){
e.printStackTrace();
}
long beginTime = Utils.begainTime1;
if (Utils.begainTime2<Utils.begainTime1){
beginTime = Utils.begainTime2;
}
long endTime = Utils.endTime1;
if (Utils.endTime2>Utils.endTime1){
endTime = Utils.endTime2;
}
System.out.println("耗時:" + (endTime-beginTime)/1000 + "s");
}
}
輸出內(nèi)容:
begin task
長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=A
長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=A
end task
begin task
長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=B
長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=B
end task
耗時:6s
從運(yùn)行時間上來看萨驶,synchronized方法的問題很明顯驻子∧奔酰可以使用synchronized同步塊來解決這個問題牡彻。但是要注意synchronized同步塊的使用方式,如果synchronized同步塊使用不好的話并不會帶來效率的提升。
將上文的Task.class文件修改如下:
public void longTimeTask(){
try {
System.out.println("begin task");
Thread.sleep(3000);
String data1 = "長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=" + Thread.currentThread().getName();
String data2 = "長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=" + Thread.currentThread().getName();
synchronized (this){
getData1 = data1;
getData2 = data2;
}
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task");
}catch (InterruptedException e){
e.printStackTrace();
}
}
輸出如下:
begin task
begin task
長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=B
長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=A
end task
長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=A
長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=A
end task
耗時:3s
從上面代碼可以看出當(dāng)一個線程訪問一個對象的synchronized同步代碼塊時庄吼,另一個線程任然可以訪問該對象非synchronized同步代碼塊缎除。不在synchronized塊中的就是異步執(zhí)行,在synchronized塊中就是同步執(zhí)行总寻。
synchronized代碼塊之間的同步性
當(dāng)一個線程訪問一個對象的synchronized(this)同步代碼塊時器罐,其他線程對同一個object中的其他synchronized(this)同步代碼塊訪問將被阻塞。
如果在一個類中有很多個synchronized方法渐行,這是雖然能實(shí)現(xiàn)同步轰坊,但會受到阻塞。如果使用同步代碼塊鎖非this對象祟印,則synchronized(非this)代碼塊中的程序與同步方法是異步的肴沫,不與其他this同步方法爭搶this鎖。
靜態(tài)同步synchronized方法與synchronized(class)代碼塊
關(guān)鍵字synchronized還可以在static方法是使用蕴忆,是對當(dāng)前的*.java文件的Class類進(jìn)行加鎖颤芬。非靜態(tài)的synchronized關(guān)鍵字是給對象加鎖。
public static void printA() {
synchronized (Service.class) {
try {
System.out.println(
"線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進(jìn)入printA");
Thread.sleep(3000);
System.out.println(
"線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開printA");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized public static void printB() {
System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進(jìn)入printB");
System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開printB");
}
synchronized public void printC() {
System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進(jìn)入printC");
System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開printC");
}
public class Main {
public static void main(String[] args){
Service service = new Service();
new Thread(Service::printA, "A").start();
new Thread(Service::printB, "B").start();
new Thread(() -> service.printC(), "C").start();
}
}
輸出內(nèi)容:
線程名稱為:A在1552262297299進(jìn)入printA
線程名稱為:C在1552262297300進(jìn)入printC
線程名稱為:C在1552262297300離開printC
線程名稱為:A在1552262300301離開printA
線程名稱為:B在1552262300301進(jìn)入printB
線程名稱為:B在1552262300301離開printB
從運(yùn)行結(jié)果可以看出:靜態(tài)同步synchronized方法與synchronized(class)代碼塊持有的鎖一樣孽文,都是Class鎖,Class鎖對對象的所有實(shí)例起作用夺艰。synchronized關(guān)鍵字加到非static靜態(tài)方法上持有的是對象鎖芋哭。線程A,B和線程C持有的鎖不一樣,所以A和B運(yùn)行同步郁副,但是和C運(yùn)行不同步减牺。
數(shù)據(jù)類型String的常量池特性
JVM具有String常量池緩存的功能,將synchronized(string)與String聯(lián)合使用時會出現(xiàn)一些問題存谎。
String s1 = "a";
String s2="a";
System.out.println(s1==s2);//true
比如兩個同步方法都是synchronized("abc"){}那么多線程會持有相同的鎖拔疚,所以大多數(shù)同步代碼塊不用String作為鎖。
本文代碼:GitHub
歡迎關(guān)注公眾號: