当前位置: 代码迷 >> Java相关 >> java多线程(3)——锁机制synchronized(同步语句块)
  详细解决方案

java多线程(3)——锁机制synchronized(同步语句块)

热度:33   发布时间:2016-04-22 19:10:58.0
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

 

  相关解决方案