你的位置:首页 > Java教程

[Java教程]java多线程(三)——锁机制synchronized(同步语句块)


用关键字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