用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解决。
一、用同步代码块解决同步方法的弊端
Task类
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class Task { 4 5 private String getData1; 6 private String getData2; 7 8 public void doLongTimeTask() { 9 try {10 System.out.println("begin task");11 Thread.sleep(3000);12 13 String privateGetData1 = "长时间处理任务后从远程返回的值1 threadName="14 + Thread.currentThread().getName();15 String privateGetData2 = "长时间处理任务后从远程返回的值2 threadName="16 + Thread.currentThread().getName();17 18 synchronized (this) {19 getData1 = privateGetData1;20 getData2 = privateGetData2;21 }22 23 System.out.println(getData1);24 System.out.println(getData2);25 System.out.println("end task");26 } catch (InterruptedException e) {27 e.printStackTrace();28 }29 }30 }
常量工具类
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class CommonUtils { 4 5 public static long beginTime1; 6 public static long endTime1; 7 8 public static long beginTime2; 9 public static long endTime2;10 }
线程类——2个
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class MyThread1 extends Thread { 4 5 private Task task; 6 7 public MyThread1(Task task) { 8 super(); 9 this.task = task;10 }11 12 @Override13 public void run() {14 super.run();15 CommonUtils.beginTime1 = System.currentTimeMillis();16 task.doLongTimeTask();17 CommonUtils.endTime1 = System.currentTimeMillis();18 }19 20 }
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class MyThread2 extends Thread { 4 5 private Task task; 6 7 public MyThread2(Task task) { 8 super(); 9 this.task = task;10 }11 12 @Override13 public void run() {14 super.run();15 CommonUtils.beginTime2 = System.currentTimeMillis();16 task.doLongTimeTask();17 CommonUtils.endTime2 = System.currentTimeMillis();18 }19 20 }
运行类
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class Run { 4 5 public static void main(String[] args) { 6 Task task = new Task(); 7 8 MyThread1 thread1 = new MyThread1(task); 9 thread1.start();10 11 MyThread2 thread2 = new MyThread2(task);12 thread2.start();13 14 try {15 Thread.sleep(10000);16 } catch (InterruptedException e) {17 e.printStackTrace();18 }19 20 long beginTime = CommonUtils.beginTime1;21 if (CommonUtils.beginTime2 < CommonUtils.beginTime1) {22 beginTime = CommonUtils.beginTime2;23 }24 25 long endTime = CommonUtils.endTime1;26 if (CommonUtils.endTime2 > CommonUtils.endTime1) {27 endTime = CommonUtils.endTime2;28 }29 30 System.out.println("耗时" + ((endTime - beginTime) / 1000) + " 秒");31 }32 }
结果
1 begin task2 begin task3 长时间处理任务后从远程返回的值1 threadName=Thread-14 长时间处理任务后从远程返回的值1 threadName=Thread-05 长时间处理任务后从远程返回的值2 threadName=Thread-06 长时间处理任务后从远程返回的值2 threadName=Thread-07 end task8 end task9 耗时3 秒
这里是用的synchronized代码锁,如果换成方法锁
所有代码不变,仅更改Task类
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class Task { 4 5 private String getData1; 6 private String getData2; 7 8 public synchronized void doLongTimeTask() { 9 try {10 System.out.println("begin task");11 Thread.sleep(3000);12 getData1 = "长时间处理任务后从远程返回的值1 threadName="13 + Thread.currentThread().getName();14 getData2 = "长时间处理任务后从远程返回的值2 threadName="15 + Thread.currentThread().getName();16 System.out.println(getData1);17 System.out.println(getData2);18 System.out.println("end task");19 } catch (InterruptedException e) {20 // TODO Auto-generated catch block21 e.printStackTrace();22 }23 }24 }
运行结果
1 begin task2 长时间处理任务后从远程返回的值1 threadName=Thread-03 长时间处理任务后从远程返回的值2 threadName=Thread-04 end task5 begin task6 长时间处理任务后从远程返回的值1 threadName=Thread-17 长时间处理任务后从远程返回的值2 threadName=Thread-18 end task9 耗时6 秒
可以得出结论,当一个线程访问object的synchronized同步代码块时,另一个线程依然可以访问非同步代码块,这样同步代码块就会比同步方法所花费更短的时间,可以得到更高的效率,在同步代码块中代码是同步的,不在同步代码块中代码是异步的。
二、synchronized代码块间的同步性
当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,因为synchronized使用的是一个“对象监视器”
ObjectService类
1 package com.weishiyao.learn.day4.testSynchorized.ep3; 2 3 public class ObjectService { 4 5 public void serviceMethodA() { 6 try { 7 synchronized (this) { 8 System.out 9 .println("A begin time=" + System.currentTimeMillis());10 Thread.sleep(2000);11 System.out12 .println("A end end=" + System.currentTimeMillis());13 }14 } catch (InterruptedException e) {15 e.printStackTrace();16 }17 }18 19 public void serviceMethodB() {20 synchronized (this) {21 System.out.println("B begin time=" + System.currentTimeMillis());22 System.out.println("B end end=" + System.currentTimeMillis());23 }24 }25 }
ThreadA
1 package com.weishiyao.learn.day4.testSynchorized.ep3; 2 3 public class ThreadA extends Thread { 4 5 private ObjectService service; 6 7 public ThreadA(ObjectService service) { 8 super(); 9 this.service = service;10 }11 12 @Override13 public void run() {14 super.run();15 service.serviceMethodA();16 }17 18 }
ThreadB
1 package com.weishiyao.learn.day4.testSynchorized.ep3; 2 3 public class ThreadB extends Thread { 4 private ObjectService service; 5 6 public ThreadB(ObjectService service) { 7 super(); 8 this.service = service; 9 }10 11 @Override12 public void run() {13 super.run();14 service.serviceMethodB();15 }16 }
Run
1 package com.weishiyao.learn.day4.testSynchorized.ep3; 2 3 public class Run { 4 5 public static void main(String[] args) { 6 ObjectService service = new ObjectService(); 7 8 ThreadA a = new ThreadA(service); 9 a.setName("a");10 a.start();11 12 ThreadB b = new ThreadB(service);13 b.setName("b");14 b.start();15 }16 17 }
结果
1 A begin time=14590771862492 A end end=14590771882493 B begin time=14590771882494 B end end=1459077188249
三、静态同步synchronized方法与synchronized(class)代码块
关键字synchronized还可以用在static静态方法上,如果这样写,那是对当前的java对应的Class类进行上锁
Service类
1 package com.weishiyao.learn.day4.staticSynchorized; 2 3 public class Service { 4 synchronized public static void printA() { 5 try { 6 System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入A"); 7 Thread.sleep(3000); 8 System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开A"); 9 } catch (Exception e) {10 e.printStackTrace();11 }12 }13 14 synchronized public static void printB() {15 try {16 System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入B");17 Thread.sleep(3000);18 System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开B");19 } catch (Exception e) {20 e.printStackTrace();21 }22 }23 24 synchronized public void printC() {25 try {26 System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入C");27 Thread.sleep(3000);28 System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开C");29 } catch (Exception e) {30 e.printStackTrace();31 }32 }33 }
ThreadA
1 package com.weishiyao.learn.day4.staticSynchorized; 2 3 public class ThreadA extends Thread { 4 private Service service; 5 6 public ThreadA(Service service) { 7 super(); 8 this.service = service; 9 }10 11 @SuppressWarnings("static-access")12 @Override13 public void run() {14 service.printA();15 }16 }
ThreadB、ThreadC类似ThreadA,不再列出
Run
1 package com.weishiyao.learn.day4.staticSynchorized; 2 3 public class Run { 4 public static void main(String[] args) { 5 Service service = new Service(); 6 ThreadA threadA = new ThreadA(service); 7 threadA.setName("A"); 8 threadA.start(); 9 ThreadB threadB = new ThreadB(service);10 threadB.setName("B");11 threadB.start();12 ThreadC threadC = new ThreadC(service);13 threadC.setName("C");14 threadC.start();15 }16 }
结果
1 线程名称为:A在1459078101483进入A2 线程名称为:C在1459078101490进入C3 线程名称为:A在1459078104484离开A4 线程名称为:B在1459078104484进入B5 线程名称为:C在1459078104491离开C6 线程名称为:B在1459078107484离开B
分析运行结果,A和B是同步运行,C是异步运行,异步的原因是持有不同的锁,一个是对象锁,另外一个是Class锁。
同步synchronized(class)代码块的作用和synchronized static方法的作用一样。
四、内置类与同步
OutClass
1 package com.weishiyao.learn.day4.syncClass.ep5; 2 3 public class OutClass { 4 static class InnerClass1 { 5 public void method1(InnerClass2 class2) { 6 String threadName = Thread.currentThread().getName(); 7 synchronized (class2) { 8 System.out.println(threadName 9 + " 进入InnerClass1类中的method1方法");10 for (int i = 0; i < 10; i++) {11 System.out.println("i=" + i);12 try {13 Thread.sleep(100);14 } catch (InterruptedException e) {15 16 }17 }18 System.out.println(threadName19 + " 离开InnerClass1类中的method1方法");20 }21 }22 23 public synchronized void method2() {24 String threadName = Thread.currentThread().getName();25 System.out.println(threadName + " 进入InnerClass1类中的method2方法");26 for (int j = 0; j < 10; j++) {27 System.out.println("j=" + j);28 try {29 Thread.sleep(100);30 } catch (InterruptedException e) {31 32 }33 }34 System.out.println(threadName + " 离开InnerClass1类中的method2方法");35 }36 }37 38 static class InnerClass2 {39 public synchronized void method1() {40 String threadName = Thread.currentThread().getName();41 System.out.println(threadName + " 进入InnerClass2类中的method1方法");42 for (int k = 0; k < 10; k++) {43 System.out.println("k=" + k);44 try {45 Thread.sleep(100);46 } catch (InterruptedException e) {47 48 }49 }50 System.out.println(threadName + " 离开InnerClass2类中的method1方法");51 }52 }53 }
Run
1 package com.weishiyao.learn.day4.syncClass.ep5; 2 3 import com.weishiyao.learn.day4.syncClass.ep5.OutClass.InnerClass1; 4 import com.weishiyao.learn.day4.syncClass.ep5.OutClass.InnerClass2; 5 6 public class Run { 7 8 public static void main(String[] args) { 9 final InnerClass1 in1 = new InnerClass1();10 final InnerClass2 in2 = new InnerClass2();11 Thread t1 = new Thread(new Runnable() {12 public void run() {13 in1.method1(in2);14 }15 }, "T1");16 Thread t2 = new Thread(new Runnable() {17 public void run() {18 in1.method2();19 }20 }, "T2");21 Thread t3 = new Thread(new Runnable() {22 public void run() {23 in2.method1();24 }25 }, "T3");26 t1.start();27 t2.start();28 t3.start();29 }30 }
结果
1 T2 进入InnerClass1类中的method2方法 2 T1 进入InnerClass1类中的method1方法 3 i=0 4 j=0 5 i=1 6 j=1 7 j=2 8 i=2 9 i=310 j=311 j=412 i=413 i=514 j=515 j=616 i=617 i=718 j=719 i=820 j=821 i=922 j=923 T2 离开InnerClass1类中的method2方法24 T1 离开InnerClass1类中的method1方法25 T3 进入InnerClass2类中的method1方法26 k=027 k=128 k=229 k=330 k=431 k=532 k=633 k=734 k=835 k=936 T3 离开InnerClass2类中的method1方法
同步代码块synchronized(class2)对class2上锁后,其他线程只能以同步的方式调用class2中的静态同步方法
五、锁对象的改变
在将任何数据类型作为同步锁时,需要注意的是,是否有多个线程同时持有锁对象,如果同时持有相同的锁对象,则这些线程之间就是同步的;如果分别获得锁对象,这些线程之间就是异步的。
MyService
1 package com.weishiyao.learn.day4.syncClass.ep6; 2 3 public class MyService { 4 private String lock = "123"; 5 6 public void testMethod() { 7 try { 8 synchronized (lock) { 9 System.out.println(Thread.currentThread().getName() + " begin "10 + System.currentTimeMillis());11 lock = "456";12 Thread.sleep(2000);13 System.out.println(Thread.currentThread().getName() + " end "14 + System.currentTimeMillis());15 }16 } catch (InterruptedException e) {17 e.printStackTrace();18 }19 }20 21 }
ThreadA
1 package com.weishiyao.learn.day4.syncClass.ep6; 2 3 public class ThreadA extends Thread { 4 5 private MyService service; 6 7 public ThreadA(MyService service) { 8 super(); 9 this.service = service;10 }11 12 @Override13 public void run() {14 service.testMethod();15 }16 }
ThreadB
package com.weishiyao.learn.day4.syncClass.ep6;public class ThreadB extends Thread { private MyService service; public ThreadB(MyService service) { super(); this.service = service; } @Override public void run() { service.testMethod(); }}
Run1
1 package com.weishiyao.learn.day4.syncClass.ep6; 2 3 public class Run1 { 4 5 public static void main(String[] args) throws InterruptedException { 6 7 MyService service = new MyService(); 8 9 ThreadA a = new ThreadA(service);10 a.setName("A");11 12 ThreadB b = new ThreadB(service);13 b.setName("B");14 15 a.start();16 Thread.sleep(50);17 b.start();18 }19 }
结果
1 A begin 14590801437962 B begin 14590801438463 A end 14590801457974 B end 1459080145846
因为50毫秒之后a取得的是锁"456"
重新改一下Run类
1 package com.weishiyao.learn.day4.syncClass.ep6; 2 3 public class Run2 { 4 5 public static void main(String[] args) throws InterruptedException { 6 7 MyService service = new MyService(); 8 9 ThreadA a = new ThreadA(service);10 a.setName("A");11 12 ThreadB b = new ThreadB(service);13 b.setName("B");14 15 a.start();16 b.start();17 }18 }
结果
1 A begin 14590803187822 A end 14590803207823 B begin 14590803207824 B end 1459080322783
只要对象不变,即使对象的属性被改变,运行结果还是同步的
Service
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class Service { 4 5 public void serviceMethodA(Userinfo userinfo) { 6 synchronized (userinfo) { 7 try { 8 System.out.println(Thread.currentThread().getName()); 9 userinfo.setUsername("abcabcabc");10 Thread.sleep(3000);11 System.out.println("end! time=" + System.currentTimeMillis());12 } catch (InterruptedException e) {13 e.printStackTrace();14 }15 }16 }17 }
Userinfo
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class Userinfo { 4 private String username; 5 private String password; 6 7 public Userinfo() { 8 super(); 9 }10 11 public Userinfo(String username, String password) {12 super();13 this.username = username;14 this.password = password;15 }16 17 public String getUsername() {18 return username;19 }20 21 public void setUsername(String username) {22 this.username = username;23 }24 25 public String getPassword() {26 return password;27 }28 29 public void setPassword(String password) {30 this.password = password;31 }32 33 }
ThreadA
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class ThreadA extends Thread { 4 5 private Service service; 6 private Userinfo userinfo; 7 8 public ThreadA(Service service, 9 Userinfo userinfo) {10 super();11 this.service = service;12 this.userinfo = userinfo;13 }14 15 @Override16 public void run() {17 service.serviceMethodA(userinfo);18 }19 20 }
ThreadB
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class ThreadB extends Thread { 4 5 private Service service; 6 private Userinfo userinfo; 7 8 public ThreadB(Service service, 9 Userinfo userinfo) {10 super();11 this.service = service;12 this.userinfo = userinfo;13 }14 15 @Override16 public void run() {17 service.serviceMethodA(userinfo);18 }19 20 }
运行类
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class Run { 4 5 public static void main(String[] args) { 6 7 try { 8 Service service = new Service(); 9 Userinfo userinfo = new Userinfo();10 11 ThreadA a = new ThreadA(service, userinfo);12 a.setName("a");13 a.start();14 Thread.sleep(50);15 ThreadB b = new ThreadB(service, userinfo);16 b.setName("b");17 b.start();18 19 } catch (InterruptedException e) {20 e.printStackTrace();21 }22 23 }24 }
结果
1 a2 end! time=14590805859993 b4 end! time=1459080589000