用關鍵字synchronized聲明方法在某些狀況下是有弊端的,好比A線程調用同步方法執行一個長時間的任務,那麼B線程則必須等待比較長時間。在這樣的狀況下可使用synchronized同步語句塊來解決。併發
一、synchronized方法的弊端異步
爲了證實synchronized關鍵字聲明方法是有弊端的,看下圖示例ide
package mytask; import commonutils.CommonUtils; public class Task { private String getData1; private String getData2; public synchronized void doLongTimeTask() { try { System.out.println("begin task"); Thread.sleep(3000); getData1 = "長時間處理任務後從遠程返回的值1 threadName=" + Thread.currentThread().getName(); getData2 = "長時間處理任務後從遠程返回的值2 threadName=" + Thread.currentThread().getName(); System.out.println(getData1); System.out.println(getData2); System.out.println("end task"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package commonutils; public class CommonUtils { public static long beginTime1; public static long endTime1; public static long beginTime2; public static long endTime2; }
package mythread; import commonutils.CommonUtils; import mytask.Task; public class MyThread1 extends Thread { private Task task; public MyThread1(Task task) { super(); this.task = task; } @Override public void run() { super.run(); CommonUtils.beginTime1 = System.currentTimeMillis(); task.doLongTimeTask(); CommonUtils.endTime1 = System.currentTimeMillis(); } }
package mythread; import commonutils.CommonUtils; import mytask.Task; public class MyThread2 extends Thread { private Task task; public MyThread2(Task task) { super(); this.task = task; } @Override public void run() { super.run(); CommonUtils.beginTime2 = System.currentTimeMillis(); task.doLongTimeTask(); CommonUtils.endTime2 = System.currentTimeMillis(); } }
package test; import mytask.Task; import mythread.MyThread1; import mythread.MyThread2; import commonutils.CommonUtils; public class Run { public static void main(String[] args) { Task task = new Task(); MyThread1 thread1 = new MyThread1(task); thread1.start(); MyThread2 thread2 = new MyThread2(task); thread2.start(); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } long beginTime = CommonUtils.beginTime1; if (CommonUtils.beginTime2 < CommonUtils.beginTime1) { beginTime = CommonUtils.beginTime2; } long endTime = CommonUtils.endTime1; if (CommonUtils.endTime2 > CommonUtils.endTime1) { endTime = CommonUtils.endTime2; } System.out.println("耗時" + ((endTime - beginTime) / 1000)); } }
結果: begin task 長時間處理任務後從遠程返回的值1 threadName=Thread-0 長時間處理任務後從遠程返回的值2 threadName=Thread-0 end task begin task 長時間處理任務後從遠程返回的值1 threadName=Thread-1 長時間處理任務後從遠程返回的值2 threadName=Thread-1 end task 耗時:6
二、synchronized同步代碼塊的使用this
當兩個併發線程訪問同一個對象ibject中的synchronized(this)同步代碼塊時,一段時間內只能有一個線程被執行,另外一個線程必須等待當前線程執行完這個代碼塊後才能執行該代碼塊。spa
package service; public class ObjectService { public void serviceMethod() { try { synchronized (this) { System.out.println("begin time=" + System.currentTimeMillis()); Thread.sleep(2000); System.out.println("end end=" + System.currentTimeMillis()); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package extthread; import service.ObjectService; public class ThreadA extends Thread { private ObjectService service; public ThreadA(ObjectService service) { super(); this.service = service; } @Override public void run() { super.run(); service.serviceMethod(); } }
package extthread; import service.ObjectService; public class ThreadB extends Thread { private ObjectService service; public ThreadB(ObjectService service) { super(); this.service = service; } @Override public void run() { super.run(); service.serviceMethod(); } }
package test.run; import service.ObjectService; import extthread.ThreadA; import extthread.ThreadB; public class Run { public static void main(String[] args) { ObjectService service = new ObjectService(); ThreadA a = new ThreadA(service); a.setName("a"); a.start(); ThreadB b = new ThreadB(service); b.setName("b"); b.start(); } }
結果: begin time =1403579513572 end end 1403579515572 begin time = 1403579515572 end end =1403579517572
這裏雖然使用了synchronized同步代碼塊,但執行的效率仍是沒有提升線程
package mytask; public class Task { private String getData1; private String getData2; public void doLongTimeTask() { try { System.out.println("begin task"); Thread.sleep(3000); String privateGetData1 = "長時間處理任務後從遠程返回的值 1 threadName=" + Thread.currentThread().getName(); String privateGetData2 = "長時間處理任務後從遠程返回的值2 threadName=" + Thread.currentThread().getName(); synchronized (this) { getData1 = privateGetData1; getData2 = privateGetData2; } System.out.println(getData1); System.out.println(getData2); System.out.println("end task"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結果 begin task begin task 長時間處理任務後遠程返回的值1 threadName=Thread-1 長時間處理任務後遠程返回的值2 threadName=Thread-0 end task 長時間處理任務後遠程返回的值1 threadName=Thread-0 長時間處理任務後遠程返回的值2 threadName=Thread-0 end task 耗時:3
經過上述能夠看到,當一個線程訪問object的一個synchronized同步代碼塊時,另外一個線程仍然能夠訪問該object對象中的非synchronized(this)同步代碼塊。code
四、一半同步,一半異步對象
不在synchronized塊中就是異步執行,在synchronized塊中就是同步執行。blog
package mytask; public class Task { public void doLongTimeTask() { for (int i = 0; i < 100; i++) { System.out.println("nosynchronized threadName=" + Thread.currentThread().getName() + " i=" + (i + 1)); } System.out.println(""); synchronized (this) { for (int i = 0; i < 100; i++) { System.out.println("synchronized threadName=" + Thread.currentThread().getName() + " i=" + (i + 1)); } } } }
package mythread; import mytask.Task; public class MyThread1 extends Thread { private Task task; public MyThread1(Task task) { super(); this.task = task; } @Override public void run() { super.run(); task.doLongTimeTask(); } }
package mythread; import mytask.Task; public class MyThread2 extends Thread { private Task task; public MyThread2(Task task) { super(); this.task = task; } @Override public void run() { super.run(); task.doLongTimeTask(); } }
package test; import mytask.Task; import mythread.MyThread1; import mythread.MyThread2; public class Run { public static void main(String[] args) { Task task = new Task(); MyThread1 thread1 = new MyThread1(task); thread1.start(); MyThread2 thread2 = new MyThread2(task); thread2.start(); } }
若是非同步的時候會成爲交叉打印,同步的話即還排隊執行 。get
五、synchronized代碼塊間的同步性
在使用synchronized(this)代碼塊時須要注意的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對同一個object中全部其餘synchronized(this)同步代碼塊的訪問被阻塞,這說明synchronized使用的"對象監視器"是一個。
package doubleSynBlockOneTwo; /** * Created by Administrator on 2017/1/19 0019. */ public class ObjectService { public void serviceMethodA(){ try { synchronized (this) { System.out.println("A begin time = " + System.currentTimeMillis()); Thread.sleep(2000); System.out.println("A end time = " + System.currentTimeMillis()); } } catch (InterruptedException e) { e.printStackTrace(); } } public void serviceMethodB(){ synchronized (this){ System.out.println("B begin time ="+System.currentTimeMillis()); System.out.println("B end time" + System.currentTimeMillis()); } } }
package doubleSynBlockOneTwo; import selfThread.ThreadB; /** * Created by Administrator on 2017/1/19 0019. */ public class ThreadA extends Thread { private ObjectService service; public ThreadA(ObjectService service){ super(); this.service=service; } public void run(){ super.run(); service.serviceMethodA(); } }
package doubleSynBlockOneTwo; /** * Created by Administrator on 2017/1/19 0019. */ public class ThreadB extends Thread { private ObjectService service; public ThreadB(ObjectService service){ super(); this.service=service; } public void run (){ super.run(); service.serviceMethodB(); } }
package doubleSynBlockOneTwo; /** * Created by Administrator on 2017/1/19 0019. */ public class Run { public static void main(String [] args){ ObjectService service = new ObjectService(); ThreadA a = new ThreadA(service); a.setName("a"); a.start(); ThreadB b = new ThreadB(service); b.setName("b"); b.start(); } }
A begin time = 1484798392966 A end time = 1484798394978 B begin time =1484798394978 B end time1484798394978
六、同步synchronized(this)代碼塊是鎖定當前對象的
package synchronizedthis; /** * Created by Administrator on 2017/1/19 0019. */ public class Task { public void otherMethod(){ System.out.println("---------------------------------run--otherMethod"); } public void doLongTimeTask(){ synchronized (this){ for (int i=0;i<100;i++){ System.out.println("synchronized threadName="+Thread.currentThread().getName()+"i="+(i+1)); } } } }
package synchronizedthis; /** * Created by Administrator on 2017/1/19 0019. */ public class MyThread1 extends Thread { private Task task; public MyThread1(Task task){ super(); this.task=task; } public void run(){ super.run(); task.doLongTimeTask(); } }
package synchronizedthis; /** * Created by Administrator on 2017/1/19 0019. */ public class MyThread2 extends Thread { private Task task; public MyThread2(Task task){ super(); this.task=task; } public void run(){ super.run(); task.doLongTimeTask(); } }
package synchronizedthis; /** * Created by Administrator on 2017/1/19 0019. */ public class Run { public static void main(String [] args){ Task task = new Task(); MyThread1 thread1 = new MyThread1(task); thread1.start(); MyThread2 thread2 = new MyThread2(task); thread2.start(); } }
synchronized threadName=Thread-0i=1 synchronized threadName=Thread-0i=2 synchronized threadName=Thread-0i=3 synchronized threadName=Thread-0i=4 synchronized threadName=Thread-0i=5 synchronized threadName=Thread-0i=6 synchronized threadName=Thread-0i=7 synchronized threadName=Thread-0i=8 synchronized threadName=Thread-0i=9 synchronized threadName=Thread-0i=10 synchronized threadName=Thread-1i=1 synchronized threadName=Thread-1i=2 synchronized threadName=Thread-1i=3 synchronized threadName=Thread-1i=4 synchronized threadName=Thread-1i=5 synchronized threadName=Thread-1i=6 synchronized threadName=Thread-1i=7 synchronized threadName=Thread-1i=8 synchronized threadName=Thread-1i=9 synchronized threadName=Thread-1i=10