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     @Override
13     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     @Override
13     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 task
2 begin task
3 长时间处理任务后从远程返回的值1 threadName=Thread-1
4 长时间处理任务后从远程返回的值1 threadName=Thread-0
5 长时间处理任务后从远程返回的值2 threadName=Thread-0
6 长时间处理任务后从远程返回的值2 threadName=Thread-0
7 end task
8 end task
9 耗时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 block
21             e.printStackTrace();
22         }
23     }
24 }

运行结果

1 begin task
2 长时间处理任务后从远程返回的值1 threadName=Thread-0
3 长时间处理任务后从远程返回的值2 threadName=Thread-0
4 end task
5 begin task
6 长时间处理任务后从远程返回的值1 threadName=Thread-1
7 长时间处理任务后从远程返回的值2 threadName=Thread-1
8 end task
9 耗时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.out
12                         .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     @Override
13     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     @Override
12     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=1459077186249
2 A end    end=1459077188249
3 B begin time=1459077188249
4 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     @Override
13     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进入A
2 线程名称为:C在1459078101490进入C
3 线程名称为:A在1459078104484离开A
4 线程名称为:B在1459078104484进入B
5 线程名称为:C在1459078104491离开C
6 线程名称为: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(threadName
19                         + " 离开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=3
10 j=3
11 j=4
12 i=4
13 i=5
14 j=5
15 j=6
16 i=6
17 i=7
18 j=7
19 i=8
20 j=8
21 i=9
22 j=9
23 T2 离开InnerClass1类中的method2方法
24 T1 离开InnerClass1类中的method1方法
25 T3 进入InnerClass2类中的method1方法
26 k=0
27 k=1
28 k=2
29 k=3
30 k=4
31 k=5
32 k=6
33 k=7
34 k=8
35 k=9
36 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     @Override
13     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 1459080143796
2 B begin 1459080143846
3 A   end 1459080145797
4 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 1459080318782
2 A   end 1459080320782
3 B begin 1459080320782
4 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     @Override
16     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     @Override
16     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 a
2 end! time=1459080585999
3 b
4 end! time=1459080589000

 

posted @ 2016-03-28 10:01  极客挖掘机  阅读(690)  评论(0编辑  收藏  举报