Java并发编程(3)——synchronized

释放双眼,带上耳机,听听看~!

在Java中可以使用synchronized关键字来修饰方法、静态方法和代码块,synchronized能够隐式的获取和释放锁,从而保证在同一时刻,只有一个线程在方法或代码块中。


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
1public class SynchronizedDemo {
2
3    public static int num = 0;
4
5    public static void main(String[] args) {
6        SynchronizedDemo demo = new SynchronizedDemo();
7        new Thread(new DemoRunnable(demo), "T1").start();
8        new Thread(new DemoRunnable(demo), "T2").start();
9    }
10
11    public synchronized void test(){
12        for(int i=0; i<3; i++) {
13            try {
14                Thread.sleep(100);
15            } catch (InterruptedException e) {
16                e.printStackTrace();
17            }
18            System.out.println(Thread.currentThread().getName()+" num = "+num++);
19        }
20    }
21
22    static class DemoRunnable implements Runnable{
23
24        private SynchronizedDemo demo;
25
26        public DemoRunnable(SynchronizedDemo demo){
27            this.demo = demo;
28        }
29
30        @Override
31        public void run() {
32            demo.test();
33        }
34
35    }
36}
37
38

对test()使用synchronized修饰时,输出为:


1
2
3
4
5
6
7
8
9
10
11
1T1 num = 0
2T1 num = 1
3T1 num = 2
4T1 num = 3
5T1 num = 4
6T2 num = 5
7T2 num = 6
8T2 num = 7
9T2 num = 8
10T2 num = 9
11

当去掉synchronized时,输出为:


1
2
3
4
5
6
7
8
9
10
11
1T2 num = 1
2T1 num = 0
3T1 num = 2
4T2 num = 3
5T1 num = 5
6T2 num = 4
7T1 num = 6
8T2 num = 6
9T2 num = 7
10T1 num = 7
11

当synchronized修饰方法时,锁的对象是类的当前实例。
我们对上面的代码中的test()进行改写:


1
2
3
4
5
6
7
8
9
10
11
1public synchronized void test(){
2    System.out.println(Thread.currentThread().getName()+" START time= "+new Date());
3    try {
4        Thread.sleep(2000);
5    } catch (InterruptedException e) {
6        e.printStackTrace();
7    }
8    System.out.println(Thread.currentThread().getName()+" END   time= "+new Date());        
9}
10
11

两个线程使用同一个 demo 对象,查看输出可知只有等待其中一个线程结束后,另一个线程才执行test():


1
2
3
4
5
6
1public static void main(String[] args) {
2    SynchronizedDemo demo = new SynchronizedDemo();
3    new Thread(new DemoRunnable(demo), "T1").start();
4    new Thread(new DemoRunnable(demo), "T2").start();
5}  
6

输出为:


1
2
3
4
5
1T1 START time= Mon Aug 14 13:02:40 CST 2017
2T1 END   time= Mon Aug 14 13:02:42 CST 2017
3T2 START time= Mon Aug 14 13:02:42 CST 2017
4T2 END   time= Mon Aug 14 13:02:44 CST 2017
5

两个线程分别使用一个 demo对象,则两个线程的开始和结束互不影响:


1
2
3
4
5
1public static void main(String[] args) {
2    new Thread(new DemoRunnable(new SynchronizedDemo()), "T1").start();
3    new Thread(new DemoRunnable(new SynchronizedDemo()), "T2").start();
4}
5

输出为:


1
2
3
4
5
1T1 START time= Mon Aug 14 12:58:58 CST 2017
2T2 START time= Mon Aug 14 12:58:58 CST 2017
3T2 END   time= Mon Aug 14 12:59:00 CST 2017
4T1 END   time= Mon Aug 14 12:59:00 CST 2017
5

当synchronized修饰静态方法时,锁的对象是当前类的class对象
我们将上面的test() 改为静态方法,然后执行上面的测试代码,发现两个线程先后执行。
两种测试方法输出都为:


1
2
3
4
5
1T1 START time= Mon Aug 14 13:04:06 CST 2017
2T1 END   time= Mon Aug 14 13:04:08 CST 2017
3T2 START time= Mon Aug 14 13:04:08 CST 2017
4T2 END   time= Mon Aug 14 13:04:10 CST 2017
5

当synchronized修饰代码块时,锁的对象是指定的对象。
修改上面的test():


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
1public Object lock = new Object();
2
3public static void main(String[] args) {
4    SynchronizedDemo demo = new SynchronizedDemo();
5    new Thread(new DemoRunnable(demo), "T1").start();
6    new Thread(new DemoRunnable(demo), "T2").start();
7}
8
9public void test(){
10    System.out.println(Thread.currentThread().getName()+" START time= "+new Date());
11
12    synchronized(lock){
13        System.out.println(Thread.currentThread().getName()+" code block START time= "+new Date());
14        try {
15            Thread.sleep(2000);
16        } catch (InterruptedException e) {
17            e.printStackTrace();
18        }
19        System.out.println(Thread.currentThread().getName()+" code block end   time= "+new Date());
20    }
21
22    System.out.println(Thread.currentThread().getName()+" END   time= "+new Date());        
23}
24
25

输出结果为:


1
2
3
4
5
6
7
8
9
10
1T2 START time= Mon Aug 14 15:07:26 CST 2017
2T2 code block START time= Mon Aug 14 15:07:26 CST 2017
3T1 START time= Mon Aug 14 15:07:26 CST 2017
4T2 code block end   time= Mon Aug 14 15:07:28 CST 2017
5T1 code block START time= Mon Aug 14 15:07:28 CST 2017
6T2 END   time= Mon Aug 14 15:07:28 CST 2017
7T1 code block end   time= Mon Aug 14 15:07:30 CST 2017
8T1 END   time= Mon Aug 14 15:07:30 CST 2017
9
10

由输出可以看出对于同步代码块,同一时间只能有一个线程访问。

由synchronized修饰的同步块,如果抛出异常,则锁自动释放。
修改上面的test方法:


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
1public static void main(String[] args) throws InterruptedException {
2        SynchronizedDemo demo = new SynchronizedDemo();
3        new Thread(new DemoRunnable(demo), "T1").start();
4        Thread.sleep(50);//延时创建线程2,使线程1先执行
5        new Thread(new DemoRunnable(demo), "T2").start();
6    }
7
8    public void test(){
9        System.out.println(Thread.currentThread().getName()+" START time= "+new Date());
10
11        synchronized(lock){
12            System.out.println(Thread.currentThread().getName()+" code block START time= "+new Date());
13            try {
14                //使线程1抛出异常
15                if("T1".equals(Thread.currentThread().getName())){
16                    throw new RuntimeException();
17                }
18                Thread.sleep(2000);
19            } catch (InterruptedException e) {
20                e.printStackTrace();
21            }
22            System.out.println(Thread.currentThread().getName()+" code block end   time= "+new Date());
23        }
24
25        System.out.println(Thread.currentThread().getName()+" END   time= "+new Date());        
26    }
27
28

由输出结果可以看出,线程T1抛出异常,没有继续执行,而线程T2直接获取了锁。

synchroniced同步块对于同一个线程来说是可重入的,不回出现自己把自己锁死的问题。
下面的例子说明了这个问题:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1public class SynchronizedReentrantTest {
2
3    public static void main(String[] args) {
4        test();
5    }
6
7    public static synchronized void test(){
8        System.out.println("abc");
9        demo();
10        System.out.println("ghi");
11    }
12
13    public static synchronized void demo(){
14        System.out.println("def");
15    }
16}
17

输出结果:


1
2
3
4
1abc
2def
3ghi
4

在代码中,test方法已经持有了class对象的锁,在没有释放的情况下,依然执行了demo方法。

给TA打赏
共{{data.count}}人
人已打赏
安全技术

详解Node.js API系列 Crypto加密模块(1)

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索