Java多线程之synchronized关键字

Posted 尘世间迷茫的小书童

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程之synchronized关键字相关的知识,希望对你有一定的参考价值。

一、synchronized锁住的不是代码块,是对象。
 1 /**
 2  * synchronized 对某个对象加锁
 3  */
 4 public class SynchronizedTest {
 5 
 6     private int count = 10;
 7     private Object o = new Object();
 8 
 9     private void method() {
10         synchronized (o) { //任何线程想执行下面这段代码都需要拿到o这把锁
11             count--;
12             System.out.println(Thread.currentThread().getName() + " count=" + count);
13         }
14     }
15 
16 }

 

二、synchronized是可重入的。

 1 import java.util.concurrent.TimeUnit;
 2 
 3 /**
 4  * 一个同步方法可以调用另外一个同步方法,一个线程已经拥有了某个对象锁,再次申请仍然会得到这把锁。
 5  * 也就是说synchronized是可重入的。
 6  */
 7 public class SynchronizedTest3 {
 8 
 9     synchronized void m1() {
10         System.out.println(Thread.currentThread().getName() + " m1 start ...");
11         try {
12             TimeUnit.SECONDS.sleep(1);
13         } catch (InterruptedException e) {
14             e.printStackTrace();
15         }
16         m2();
17         System.out.println(Thread.currentThread().getName() + " m1 end ...");
18     }
19 
20     synchronized void m2() {
21         System.out.println(Thread.currentThread().getName() + " m2 start ...");
22         try {
23             TimeUnit.SECONDS.sleep(2);
24         } catch (InterruptedException e) {
25             e.printStackTrace();
26         }
27         System.out.println(Thread.currentThread().getName() + " m2 end ...");
28     }
29 
30     public static void main(String[] args) {
31 
32         SynchronizedTest3 test = new SynchronizedTest3();
33         new Thread(() -> {
34             test.m1();
35         }, "t1").start();
36         new Thread(() -> {
37             test.m2();
38         }, "t2").start();
39 
40     }
41 
42 }

三、子类同步方法调用父类同步方法,这是可以的。
 1 import java.util.concurrent.TimeUnit;
 2 
 3 /**
 4  * 子类调用父类同步方法,这是可以的
 5  */
 6 public class SynchronizedTest5 {
 7 
 8     public static void main(String[] args) {
 9 
10         Chinese chinese = new Chinese();
11         chinese.m();
12 
13     }
14 
15     static class Person {
16         synchronized void m() {
17             System.out.println("m start ...");
18             try {
19                 TimeUnit.SECONDS.sleep(2);
20             } catch (InterruptedException e) {
21                 e.printStackTrace();
22             }
23             System.out.println("m end ...");
24         }
25     }
26 
27     static class Chinese extends Person {
28         @Override
29         synchronized void m() {
30             System.out.println("child m start ...");
31             super.m();
32             System.out.println("child m end ...");
33         }
34     }
35 
36 }
 
四、synchronized遇到异常,线程会释放锁。
 1 import java.util.concurrent.TimeUnit;
 2 
 3 /**
 4  * 多线程环境synchronized遇到异常,线程会释放锁
 5  */
 6 public class SynchronizedTest6 {
 7 
 8     int count = 0;
 9 
10     synchronized void m() {
11         System.out.println(Thread.currentThread().getName() + " start");
12         while (true) {
13             count++;
14             System.out.println(Thread.currentThread().getName() + " count=" + count);
15             try {
16                 TimeUnit.SECONDS.sleep(1);
17             } catch (InterruptedException e) {
18                 e.printStackTrace();
19             }
20             if(count == 5) {
21                 int i = 1/0; //此处会抛出异常,锁将被释放,想要锁不被释放,可以在这里进行catch,让循环继续.
22                 //java.lang.ArithmeticException: / by zero
23                 //解决办法: try-catch这个异常
24             }
25         }
26     }
27 
28     public static void main(String[] args) {
29 
30         SynchronizedTest6 test = new SynchronizedTest6();
31         Runnable r = new Runnable() {
32             @Override
33             public void run() {
34                 test.m();
35             }
36         };
37 
38         new Thread(r, "线程1").start();
39 
40         try {
41             TimeUnit.SECONDS.sleep(3);
42         } catch (InterruptedException e) {
43             e.printStackTrace();
44         }
45 
46         new Thread(r, "线程2").start();
47 
48     }
49 
50 }

 

五、synchronized优化
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * synchronized优化
 * synchronized代码块包住的代码越少越好
 */
public class SynchronizedTest7 {

    int count = 0;

    synchronized void add() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(int i=0; i<1000; i++) {
            count++;
        }
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


     void add1() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         synchronized(this) {
             for(int i=0; i<1000; i++) {
                 count++;
             }
         }
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        SynchronizedTest7 test = new SynchronizedTest7();
        List<Thread> threads = new ArrayList<>(10);
        long start = System.currentTimeMillis();
        for(int i=0; i<10; i++) {
            threads.add(new Thread(() -> {
                test.add();
            }, "Thread" + i));
        }
        threads.forEach(t -> t.start());
        threads.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long end = System.currentTimeMillis();
        System.out.println("add方法耗时:" + (end - start) + " count=" + test.count);

        //add1方法
        threads.clear();
        start = System.currentTimeMillis();
        for(int i=0; i<10; i++) {
            threads.add(new Thread(() -> {
                test.add1();
            }, "Thread" + i));
        }
        threads.forEach(t -> t.start());
        threads.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        end = System.currentTimeMillis();
        System.out.println("add1方法耗时:" + (end - start) + " count=" + test.count);

        ExecutorService service = Executors.newFixedThreadPool(10);
        start = System.currentTimeMillis();
        for(int i=0; i<10; i++) {
            service.submit(() -> {
                test.add();
            });
        }
        while (true) {
            if(!service.isTerminated()) break;
        }
        end = System.currentTimeMillis();
        System.out.println("ExecutorService add方法耗时:" + (end - start) + " count=" + test.count);
        service.shutdown();
//        if(service.isTerminated()) {
//            end = System.currentTimeMillis();
//            System.out.println("ExecutorService add方法耗时:" + (end - start) + " count=" + test.count);
//        }

        ExecutorService service1 = Executors.newFixedThreadPool(10);
        start = System.currentTimeMillis();
        for(int i=0; i<10; i++) {
            service1.submit(() -> {
                test.add1();
            });
        }
        while (true) {
            if(!service1.isTerminated()) break;
        }
        end = System.currentTimeMillis();
        System.out.println("ExecutorService add1方法耗时:" + (end - start) + " count=" + test.count);
        service1.shutdown();
//        if(service1.isTerminated()) {
//            end = System.currentTimeMillis();
//            System.out.println("ExecutorService add1方法耗时:" + (end - start) + " count=" + test.count);
//        }
    }

}

 

六、不要以字符串常量作为锁定对象
 1 /**
 2  * 不要以字符串常量作为锁定对象
 3  * str1和str2是同一个都对象
 4  * 会发生诡异的死锁阻塞
 5  */
 6 public class SynchronizedTest9 {
 7 
 8     String str1 = "Hello";
 9     String str2 = "Hello";
10 
11     void m1() {
12         synchronized (str1) {
13 
14         }
15     }
16 
17     void m2() {
18         synchronized (str2) {
19 
20         }
21     }
22 
23 }
 
 
七、synchronized写法
 1 public class SynchronizedTest2 {
 2 
 3     private static int count = 10;
 4 
 5     private synchronized void method() { //等同于synchronized(this)
 6         count--;
 7         System.out.println(Thread.currentThread().getName() + " count=" + count);
 8     }
 9 
10     private static void method2() {
11         synchronized (SynchronizedTest2.class) { //考虑一下这里写synchronized(this)是否可以?
12             count--;
13         }
14     }
15 
16 }

 

 
 

以上是关于Java多线程之synchronized关键字的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程之深入理解synchronize关键字

Java多线程系列---“基础篇”04之 synchronized关键字

Java——多线程高并发系列之synchronized关键字

Java——多线程高并发系列之synchronized关键字

Java多线程之synchronized和volatile的比较

[ 转载 ] Java多线程系列--“基础篇”04之 synchronized关键字