JAVA高并发程序设计学习:Synchronized同步代码块具体使用方法

时间:2022-05-22 05:47:35

多线程同时对资源进行访问时,同步机制使得同一时间内只能有一个线程对资源进行操作。

同步机制可以用Synchronized实现。

当Synchronized修饰一个方法的时候,该方法称为同步方法。

当Synchronized方法执行完成或者异常时会释放锁。

会有同学对synchronized修饰方法,静态方法,对象时具体对哪些东西加锁不是很明白,这里会进行详细的讲解。

  • synchronized修饰方法时,会对类实例进行加锁,该实例的所有synchronized方法必须等当前锁释放后才能访问。
  • synchronized修饰静态方法时,会对类对象上锁,改类的其他实例的的synchronized方法必须等当前锁释放后才能访问。
  • synchronized修饰对象时,会对对象上锁,其他使用该对象的synchronized方法必须等当前锁释放才能访问。

不使用synchronized修饰方法时

代码设计中一共使用到了三个类。

Example类是公共资源,会有多个线程访问。

本案例中有两个线程访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class  Example {
 
     public  void  exec() {
         for  ( int  i = 0; i < 10; i++) {
             try  {
                 long  time = ( long ) (Math.random() * 1000);
                 Thread.sleep(time);
             } catch  (InterruptedException ex) {
                 ex.printStackTrace();
             }
 
             System. out .printf( "%s,Hello[%d]\n" , Thread.currentThread().getName(), i);
         }
 
     }
}

MyThread类是线程类,用于访问Example公共资源。

1
2
3
4
5
6
7
8
9
10
11
12
class  MyThread extends Thread {
     private  final Example example;
 
     public  MyThread(Example example) {
         this .example = example;
     }
 
     @Override
     public  void  run() {
         example.exec();
     }
}

TestSynchronized是主类,用于new 线程并启动。

1
2
3
4
5
6
7
8
9
public  class  TestSynchronized {
     public  static  void  main(String[] args) {
         Example example = new  Example();
         MyThread thread1 = new  MyThread(example);
         MyThread thread2 = new  MyThread(example);
         thread1.start();
         thread2.start();
     }
}

由于现在的Example类的方法没有使用同步方法,所以两个线程会同时访问该方法,打印的值也是无序的,如下:

JAVA高并发程序设计学习:Synchronized同步代码块具体使用方法

使用Synchronized修饰方法时

我们需要修改Example这个公共资源的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class  Example {
 
     public  synchronized void  exec() {
         for  ( int  i = 0; i < 10; i++) {
             try  {
                 long  time = ( long ) (Math.random() * 1000);
                 Thread.sleep(time);
             } catch  (InterruptedException ex) {
                 ex.printStackTrace();
             }
 
             System. out .printf( "%s,Hello[%d]\n" , Thread.currentThread().getName(), i);
         }
 
     }
}

对于exec方法加上synchronized关键字,该方法同时只能有一个线程访问。

 

JAVA高并发程序设计学习:Synchronized同步代码块具体使用方法

类中存在两个synchronized方法时

当类中存在多个synchronized方法时,线程访问synchronized方法时会对类的实例加锁,因此,该实例的其他synchronized方法在线程未结束之前无法访问

我们对代码进行修改:

  • Example类增加一个同步方法。
  • 增加一个线程类MyThread2,用于访问Example的exec2()方法。
  • 主线程创建两个线程,并启动。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public  class  TestSynchronized {
     public  static  void  main(String[] args) {
         Example example = new  Example();
         MyThread thread1 = new  MyThread(example);
         MyThread2 thread2 = new  MyThread2(example);
         thread1.start();
         thread2.start();
     }
}
 
class  MyThread extends Thread {
     private  final Example example;
 
     public  MyThread(Example example) {
         this .example = example;
     }
 
     @Override
     public  void  run() {
         example.exec();
     }
}
 
class  MyThread2 extends Thread {
     private  final Example example;
 
     public  MyThread2(Example example) {
         this .example = example;
     }
 
     @Override
     public  void  run() {
         example.exec2();
     }
}
 
class  Example {
 
     public  synchronized void  exec() {
         for  ( int  i = 0; i < 10; i++) {
             try  {
                 long  time = ( long ) (Math.random() * 1000);
                 Thread.sleep(time);
             } catch  (InterruptedException ex) {
                 ex.printStackTrace();
             }
 
             System. out .printf( "%s,Hello[%d]\n" , Thread.currentThread().getName(), i);
         }
 
     }
 
     public  synchronized void  exec2() {
         for  ( int  i = 0; i < 10; i++) {
             try  {
                 long  time = ( long ) (Math.random() * 1000);
                 Thread.sleep(time);
             } catch  (InterruptedException ex) {
                 ex.printStackTrace();
             }
 
             System. out .printf( "%s,world[%d]\n" , Thread.currentThread().getName(), i);
         }
     }
}

执行结果如下:

JAVA高并发程序设计学习:Synchronized同步代码块具体使用方法

 

synchronized修饰静态方法

synchronized修饰静态方法时,会对Class对象加锁。

对代码进行修改:

  • Example方法增加一个同步static方法
  • 在主线程新建两个Example的实例
  • 启动两个线程并执行

当其中一个实例的exec方法没有执行结束时,另外一个实例的任何synchronized都无法执行,因为修饰静态方法锁的是Class对象,而不是锁Class的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/*******************************************************************************
  *                                                                             
  *  COPYRIGHT (C) 2016 Tuniu Limited - ALL RIGHTS RESERVED.                 
  *                                                                                                                                
  *  Creation Date: 2016年9月27日                                                     
  *                                                                             
  *******************************************************************************/
 
package thread;
 
/**
  * @author zhoujie8
  *
  */
public  class  TestSynchronized {
     public  static  void  main(String[] args) {
         Example example = new  Example();
         MyThread thread1 = new  MyThread(example);
         Example example2 = new  Example();
         MyThread2 thread2 = new  MyThread2(example2);
 
         thread1.start();
         thread2.start();
     }
}
 
class  MyThread extends Thread {
     private  final Example example;
 
     public  MyThread(Example example) {
         this .example = example;
     }
 
     @Override
     public  void  run() {
         example.exec3();
     }
}
 
class  MyThread2 extends Thread {
     private  final Example example;
 
     public  MyThread2(Example example) {
         this .example = example;
     }
 
     @Override
     public  void  run() {
         example.exec3();
     }
}
 
class  Example {
 
     public  synchronized static  void  exec3() {
         for  ( int  i = 0; i < 10; i++) {
             try  {
                 long  time = ( long ) (Math.random() * 1000);
                 Thread.sleep(time);
             } catch  (InterruptedException ex) {
                 ex.printStackTrace();
             }
 
             System. out .printf( "%s,Static[%d]\n" , Thread.currentThread().getName(), i);
         }
     }
 
     public  synchronized void  exec() {
         for  ( int  i = 0; i < 10; i++) {
             try  {
                 long  time = ( long ) (Math.random() * 1000);
                 Thread.sleep(time);
             } catch  (InterruptedException ex) {
                 ex.printStackTrace();
             }
 
             System. out .printf( "%s,Hello[%d]\n" , Thread.currentThread().getName(), i);
         }
 
     }
 
     public  synchronized void  exec2() {
         for  ( int  i = 0; i < 10; i++) {
             try  {
                 long  time = ( long ) (Math.random() * 1000);
                 Thread.sleep(time);
             } catch  (InterruptedException ex) {
                 ex.printStackTrace();
             }
 
             System. out .printf( "%s,world[%d]\n" , Thread.currentThread().getName(), i);
         }
     }
}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Thread-1,Static[0]
Thread-1,Static[1]
Thread-1,Static[2]
Thread-1,Static[3]
Thread-1,Static[4]
Thread-1,Static[5]
Thread-1,Static[6]
Thread-1,Static[7]
Thread-1,Static[8]
Thread-1,Static[9]
Thread-0,Static[0]
Thread-0,Static[1]
Thread-0,Static[2]
Thread-0,Static[3]
Thread-0,Static[4]
Thread-0,Static[5]
Thread-0,Static[6]
Thread-0,Static[7]
Thread-0,Static[8]
Thread-0,Static[9]

synchronized修饰对象

synchronized修饰对象时锁住的是修饰的对象。

当一个线程执行时,将object对象锁住,另一个线程就不能执行对应的块

我们需要设计一个例子:

  • 类Example中需要有一个Object,需要两个同步方法,并且都是修饰该Object。
  • 创建两个线程,同时访问这两个同步访问,看是否同步进行。

类Example代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class  Example {
     private  final Object object ;
 
     public  Example(Object object ) {
         this . object  = object ;
     }
 
     public  void  exec() {
         synchronized ( object ) {
             for  ( int  i = 0; i < 10; i++) {
                 try  {
                     TimeUnit.SECONDS.sleep(1);
                 } catch  (InterruptedException ex) {
                     ex.printStackTrace();
                 }
                 System. out .printf( "%s,Hello%d\n" , Thread.currentThread().getName(), i);
             }
         }
 
     }
}

Main方法中创建两个线程进行访问:

1
2
3
4
5
6
7
8
9
public  static  void  main(String[] args) {
     Object object  = new  Object();
     Example example = new  Example( object );
     MyThread thread1 = new  MyThread(example);
     MyThread2 thread2 = new  MyThread2(example);
 
     thread1.start();
     thread2.start();
}

结果输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Thread-0,Hello0
Thread-0,Hello1
Thread-0,Hello2
Thread-0,Hello3
Thread-0,Hello4
Thread-0,Hello5
Thread-0,Hello6
Thread-0,Hello7
Thread-0,Hello8
Thread-0,Hello9
Thread-1,Hello0
Thread-1,Hello1
Thread-1,Hello2
Thread-1,Hello3
Thread-1,Hello4
Thread-1,Hello5
Thread-1,Hello6
Thread-1,Hello7
Thread-1,Hello8
Thread-1,Hello9