• Synchronized关键字


    概述

    原文链接
    我们都知道Synchronized是实现线程同步的一个机制,为了使多个线程一次获得某个资源,那么Synchronized到底锁的是什么??

    修饰方法

    非静态方法a

    非静态方法锁的是对象

    下面是两个线程分别执行同一个对象的两个方法,打印是顺序按照线程运行的顺序进行

    package 多线程.编程目标与挑战;
    
    import java.util.Date;
    
    /**
     * @author zss
     * @date 2022-07-28 10:42:09
     * @description 
     */
    public class RaceExample {
    
        public static void main(String[] args) {
            App A = new App();
    
            new Thread(() -> {
                A.sayHello();
            }, "thread1").start();
            new Thread(() -> {
                A.sayGood();
            }, "thread").start();
        }
    }
    
    class App {
        public  void sayHello() {
            System.out.println("hello");
        }
    
        public void sayGood() {
            System.out.println("good");
        }
    }
    
    • 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

    image-20220728114943864

    那么现在我们给这个方法加上,线程同步机制Synchronized,同时使sayHello方法沉睡10秒,那么现在我们仍旧执行上面的方法,那么按照这个方法,应该先输出good,10秒后在输出hello

    package 多线程.编程目标与挑战;
    
    import java.util.Date;
    
    /**
     * @author zss
     * @date 2022-07-28 10:42:09
     * @description 竞态的展示
     */
    public class RaceExample {
    
        public static void main(String[] args) {
            App A = new App();
    
            new Thread(() -> {
                System.out.println("第一个线程开始了");
                A.sayHello();
            }, "thread1").start();
            new Thread(() -> {
                System.out.println("第二个线程开始了");
                A.sayGood();
            }, "thread").start();
        }
    }
    
    class App {
        public synchronized void sayHello() {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello");
        }
    
        public synchronized void sayGood() {
            System.out.println("good");
        }
    }
    
    
    • 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

    image-20220728115657858

    但是我们执行之后发现,竟然整个都等待了10秒钟,good也没有先行出来。

    那是因为在这个过程中, App A = new App()中A对象被锁住了,线程二明明已经开始,但是得不到A对象,而sayGood方法也因为线程同步机制所申请的资源无法得到

    但是如果对于一个普通方法来说,并不会产生这个问题,假如SayHello是用Synchronized修饰,而sayGood仅仅是一个普通方法,那么就算A被锁,sayGood仍旧可以获得资源

    class App {
        public synchronized void sayHello() {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello");
        }
    
        public  void sayGood() {
            System.out.println("good");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    image-20220728120019904

    静态方法

    对于静态方法来说,我们锁定的是类,也就是上述方法中的APP,而不是实例对象A

    首先我们先不适用静态方法,但是我们创建两个对象实例A与B,我们会发现两个方法虽然加了锁,但是对象不同,还是无法锁住

    package 多线程.编程目标与挑战;
    
    import java.util.Date;
    
    /**
     * @author zss
     * @date 2022-07-28 10:42:09
     * @description 竞态的展示
     */
    public class RaceExample {
    
        public static void main(String[] args) {
            App A = new App();
            App B=new App();
    
            new Thread(() -> {
                System.out.println("第一个线程开始了");
                A.sayHello();
            }, "thread1").start();
            new Thread(() -> {
                System.out.println("第二个线程开始了");
                B.sayGood();
            }, "thread").start();
        }
    }
    
    class App {
        public synchronized void sayHello() {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello");
        }
    
        public synchronized void sayGood() {
            System.out.println("good");
        }
    }
    
    • 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

    image-20220728120512843

    那么现在我们更改为静态方法,因为锁定的是APP这个对象,那么现在还是要等待一个线程释放APP这个类之后,才可以继续执行

    package 多线程.编程目标与挑战;
    
    import java.util.Date;
    
    /**
     * @author zss
     * @date 2022-07-28 10:42:09
     * @description 竞态的展示
     */
    public class RaceExample {
    
        public static void main(String[] args) {
            App A = new App();
            App B=new App();
    
            new Thread(() -> {
                System.out.println("第一个线程开始了");
                A.sayHello();
            }, "thread1").start();
            new Thread(() -> {
                System.out.println("第二个线程开始了");
                B.sayGood();
            }, "thread").start();
        }
    }
    
    class App {
        public static synchronized void sayHello() {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello");
        }
    
        public static synchronized void sayGood() {
            System.out.println("good");
        }
    }
    
    • 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

    image-20220728120618543

    代码块

    代码块锁的是我们传入的对象

    我们首先运行一段普通的代码,此时5个线程开始运行,仅仅是执行的普通的方法

    package 多线程.编程目标与挑战;
    
    import java.util.Date;
    
    /**
     * @author zss
     * @date 2022-07-28 10:42:09
     * @description 竞态的展示
     */
    public class RaceExample {
    
        public static void main(String[] args) {
            App A = new App();
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    A.sayHello();
                }, "thread1").start();
            }
        }
    }
    
    class App {
        public void sayHello() {
            {
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我又说一遍hello");
            }
    
        }
    
    }
    
    • 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

    image-20220728121859304

    对象实例

    现在我们要对这个对象实例进行上锁,那么后面的线程应该等待先前的线程释放 App A = new App()中的A

    class App {
        public void sayHello() {
            synchronized(this){
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我又说一遍hello");
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    image-20220728122104395

    现在如果我们调整一下main方法中的策略,将main方法移动到for循环内部,每一次都创建一个新的对象,所以这个锁就不会发生作用

    image-20220728122228399

    image-20220728122316507

    假如说现在我们锁住这个类APP,那么上面的代码又将会发生不同,类只有一个必须等待线程释放锁

    class App {
        public void sayHello() {
            synchronized(App.class){
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我又说一遍hello");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    image-20220728122508107

    其他类型

    局部变量

    我们对局部变量上锁,而这个局部变量仅仅有一个,那么就必须等待释放局部变量,Integer定义的数据是对象

    class App {
        public void sayHello() {
            Integer a= 5;
            synchronized(a){
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我又说一遍hello");
            }
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    image-20220728123138074

    全局变量

    我们将这个a作为参数传递进行,那么这个a相当于全局变量,注意参数在main中的位置

    package 多线程.编程目标与挑战;
    
    import java.util.Date;
    
    /**
     * @author zss
     * @date 2022-07-28 10:42:09
     * @description 竞态的展示
     */
    public class RaceExample {
    
        public static void main(String[] args) {
            Integer a=5;
            for (int i = 0; i < 5; i++) {
                App A = new App();
                new Thread(() -> {
                    A.sayHello(a);
                }, "thread1").start();
            }
        }
    }
    
    class App {
        public void sayHello(Integer a) {
            synchronized(a){
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我又说一遍hello");
            }
    
        }
    
    }
    
    • 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

    image-20220728123334897

    那么假如说我们每一个方法都传入不同的a,那么结果也会不同

    package 多线程.编程目标与挑战;
    
    import java.util.Date;
    
    /**
     * @author zss
     * @date 2022-07-28 10:42:09
     * @description 竞态的展示
     */
    public class RaceExample {
    
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                Integer a=i;
                App A = new App();
                new Thread(() -> {
                    A.sayHello(a);
                }, "thread1").start();
            }
        }
    }
    
    class App {
        public void sayHello(Integer a) {
            synchronized(a){
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我又说一遍hello");
            }
    
        }
    
    }
    
    • 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

    image-20220728123521414

    但是如果传入的相同的a,那么他的结果还是需要排队的,当然你这里如果使用Integer a=new Integer(5);这个构造器,那么结果又会不相同,这个就涉及到常量池和堆的问题,和String类很相似,而且同时由于Integer的特殊性,Integer a=5;使用-128-127都会在常量池中产生,而这个范围之外会在堆中产生,大家可以多尝试

    image-20220728123737092

  • 相关阅读:
    基于SSM的学籍证明打印系统设计与实现。
    Java泛型的总结
    电脑怎么安装xp系统原版镜像
    11月PMP考试考点安排,快看你在哪里考试!
    工程伦理--9.5 职业能力
    C#_值类型与引用类型 及 值参数与引用参数
    QtC++与QCheckBox详解
    uni-app设置分包和预下载
    nfs共享本机目录遇到错误
    进来了解实现官网搜索引擎的三种方法
  • 原文地址:https://blog.csdn.net/qq_58510930/article/details/126032343