用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长时间。在这样的情况下可以使用synchronized同步语句块来解决。
1、synchronized方法的弊端
为了证明synchronized关键字声明方法是有弊端的,看下图示例
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
2、synchronized同步代码块的使用
当两个并发线程访问同一个对象ibject中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块后才能执行该代码块。
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)同步代码块。
4、一半同步,一半异步
不在synchronized块中就是异步执行,在synchronized块中就是同步执行。
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(); } }
如果非同步的时候会成为交叉打印,同步的话即还排队执行 。
5、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
6、同步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