• Java基础进阶多线程-线程安全和synchronized关键字



    关于多线程并发环境下,数据的安全问题。

    为什么线程安全这个是重点

    以后在开发中,我们的项目都是运行在服务器当中,
    而服务器已经将线程的定义,线程对象的创建,线程
    的启动等,都已经实现完了。这些代码我们都不需要
    编写。

    最重要的是:你要知道,你编写的程序需要放到一个
    多线程的环境下运行,你更需要关注的是这些数据
    在多线程并发的环境下是否是安全的(重点:*****)

    什么时候数据在多线程并发的环境下会存在安全问题呢?

    三个条件:

    • 条件1:多线程并发。
    • 条件2:有共享数据
    • 条件3:共享数据有修改的行为。

    满足以上3个条件之后,就会存在线程安全问题

    怎么解决线程安全问题呢?

    当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在
    线程安全问题,怎么解决这个问题?

    • 线程排队执行。(不能并发)。
    • 用排队执行解决线程安全问题。
    • 这种机制被称为:线程同步机制。

    专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行

    • 使用“线程同步机制”。

    • 线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全
      第一位,只有数据安全了,我们才可以谈效率。数据不安全,没有效率的事儿。

    说到线程同步这块,涉及到这两个专业术语:

    异步编程模型:

    • 线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,
      谁也不需要等谁,这种编程模型叫做:异步编程模型。
      其实就是:多线程并发(效率较高。)

    异步就是并发。

    同步编程模型:

    • 线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行
      结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,
      两个线程之间发生了等待关系,这就是同步编程模型。
      效率较低。线程排队执行。

    同步就是排队。

    Java中有三大变量?【重要的内容。】

    • 实例变量:在堆中。

    • 静态变量:在方法区。

    • 局部变量:在栈中。

    以上三大变量中:

    • 局部变量永远都不会存在线程安全问题。
      因为局部变量不共享。(一个线程一个栈。)=
      局部变量在栈中。所以局部变量永远都不会共享

    • 实例变量在堆中,堆只有1个

    • 静态变量在方法区中,方法区只有1个

    • 堆和方法区都是多线程共享的,所以可能存在线程安全问题

    局部变量+常量:不会有线程安全问题
    成员变量:可能会有线程安全问题。

    如果使用局部变量的话:
    建议使用:StringBuilder。
    因为局部变量不存在线程安全问题。选择StringBuilder。
    StringBuffer效率比较低。

    ArrayList是非线程安全的。
    Vector是线程安全的。
    HashMap HashSet是非线程安全的。
    Hashtable是线程安全的。

    synchronized有三种写法:

    第一种:同步代码块

    灵活
    	synchronized(线程共享对象){
    		同步代码块;
    	}
    
    • 1
    • 2
    • 3
    • 4

    synchronized后面小括号中传的这个“数据”是相当关键的。
    这个数据必须是多线程共享的数据。才能达到多线程排队。

    • ()中写什么?
      那要看你想让哪些线程同步。
      假设t1、t2、t3、t4、t5,有5个线程,
      你只希望t1 t2 t3排队,t4 t5不需要排队。怎么办?
      你一定要在()中写一个t1 t2 t3共享的对象。而这个
      对象对于t4 t5来说不是共享的。

    • //Object obj2 = new Object();
      //synchronized (this){
      //synchronized (obj) {
      //synchronized ("abc") { // "abc"在字符串常量池当中。
      //synchronized (null) { // 报错:空指针。
      //synchronized (obj2) { // 这样编写就不安全了。因为obj2不是共享对象。
      //synchronized(this){
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

    第二种:在实例方法上使用synchronized

    • 表示共享对象一定是this
      并且同步代码块是整个方法体。

    第三种:在静态方法上使用synchronized
    表示找类锁。
    类锁永远只有1把
    就算创建了100个对象,那类锁也只有一把

    对象锁:1个对象1把锁,100个对象100把锁。
    类锁:100个对象,也可能只是1把类锁

    开发中应该怎么解决线程安全问题?

    是一上来就选择线程同步吗?synchronized
    不是,synchronized会让程序的执行效率降低,用户体验不好。
    系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择
    线程同步机制。

    • 第一种方案:尽量使用局部变量代替“实例变量和静态变量”。

    • 第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样
      实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,
      对象不共享,就没有数据安全问题了。)

    • 第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候
      就只能选择synchronized了。线程同步机制。

    示例代码01:
    /*
    银行账户
        不使用线程同步机制,多线程对同一个账户进行取款,出现线程安全问题。
     */
    public class Account implements Serializable{
        private static final long serialVersionUID = 1655286831056942086L;
        //账户
        private String actno;
        //余额
        private double balance;
    
        public Account() {
        }
    
        public Account(String actno, int balance) {
            this.actno = actno;
            this.balance = balance;
        }
    
        public String getActno() {
            return actno;
        }
    
        public void setActno(String actno) {
            this.actno = actno;
        }
    
        public double getBalance() {
            return balance;
        }
    
        public void setBalance(double balance) {
            this.balance = balance;
        }
    
        //取款方法
        public void withdraw(double money){
    
            //取款之前的余额
            // t1和t2并发这个方法。。。。(t1和t2是两个栈。两个栈操作堆中同一个对象。)
           double before = this.getBalance();
            //取款之后的余额
            double after = before - money;
    
            // 在这里模拟一下网络延迟,100%会出现问题
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            //更新余额
            // 思考:t1执行到这里了,但还没有来得及执行这行代码,t2线程进来withdraw方法了。此时一定出问题。
            this.setBalance(after);
        }
    }
    public class AccountThread extends Thread{
    
        // 两个线程必须共享同一个账户对象。
        private Account act;
    
        // 通过构造方法传递过来账户对象
        public AccountThread(Account act){
            this.act = act;
        }
    
        public Account getAct() {
            return act;
        }
    
        public void setAct(Account act) {
            this.act = act;
        }
    
        public void run(){
    
            //取款5000
            // run方法的执行表示取款操作。
            // 假设取款5000
            double money = 5000;
    
            // 取款
            // 多线程并发执行这个方法。
            act.withdraw(money);
    
            System.out.println(Thread.currentThread().getName() + "对" + act.getActno() + "取款" + money + "成功," + "账户余额为:" + act.getBalance());
        }
    }
    public class Test {
        public static void main(String[] args) {
    
            // 创建账户对象(只创建1个)
            Account act = new Account("act-no",10000);
    
            // 创建两个线程
            Thread t1 = new AccountThread(act);
            Thread t2 = new AccountThread(act);
            // 设置name
            t1.setName("t1");
            t2.setName("t2");
            // 启动线程取款
            t1.start();
            t2.start();
    
        }
    }
    
    • 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
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    运行结果:

    在这里插入图片描述

    示例代码02:
    /*
    银行账户
        不使用线程同步机制,多线程对同一个账户进行取款,出现线程安全问题。
     */
    public class Account {
        //账户
        private String actno;
        //余额
        private double balance;
    
        //对象
        Object obj = new Object(); // 实例变量。(Account对象是多线程共享的,Account对象中的实例变量obj也是共享的。)
    
        public Account() {
        }
    
        public Account(String actno, int balance) {
            this.actno = actno;
            this.balance = balance;
        }
    
        public String getActno() {
            return actno;
        }
    
        public void setActno(String actno) {
            this.actno = actno;
        }
    
        public double getBalance() {
            return balance;
        }
    
        public void setBalance(double balance) {
            this.balance = balance;
        }
    
        //取款方法
        public void withdraw(double money){
    
            // 以下这几行代码必须是线程排队的,不能并发。
            // 一个线程把这里的代码全部执行结束之后,另一个线程才能进来。
            /*
            线程同步机制的语法是:
                synchronized(){
                    // 线程同步代码块。
                }
                synchronized后面小括号中传的这个“数据”是相当关键的。
                这个数据必须是多线程共享的数据。才能达到多线程排队。
    
                ()中写什么?
                    那要看你想让哪些线程同步。
                    假设t1、t2、t3、t4、t5,有5个线程,
                    你只希望t1 t2 t3排队,t4 t5不需要排队。怎么办?
                    你一定要在()中写一个t1 t2 t3共享的对象。而这个
                    对象对于t4 t5来说不是共享的。
    
                这里的共享对象是:账户对象。
                账户对象是共享的,那么this就是账户对象吧!!!
                不一定是this,这里只要是多线程共享的那个对象就行。
                在java语言中,任何一个对象都有“一把锁”,其实这把锁就是标记。(只是把它叫做锁。)
                100个对象,100把锁。1个对象1把锁。
    
                以下代码的执行原理?
                    1、假设t1和t2线程并发,开始执行以下代码的时候,肯定有一个先一个后。
                    2、假设t1先执行了,遇到了synchronized,这个时候自动找“后面共享对象”的对象锁,
                    找到之后,并占有这把锁,然后执行同步代码块中的程序,在程序执行过程中一直都是
                    占有这把锁的。直到同步代码块代码结束,这把锁才会释放。
                    3、假设t1已经占有这把锁,此时t2也遇到synchronized关键字,也会去占有后面
                    共享对象的这把锁,结果这把锁被t1占有,t2只能在同步代码块外面等待t1的结束,
                    直到t1把同步代码块执行结束了,t1会归还这把锁,此时t2终于等到这把锁,然后
                    t2占有这把锁之后,进入同步代码块执行程序。
    
                    这样就达到了线程排队执行。
                    这里需要注意的是:这个共享对象一定要选好了。这个共享对象一定是你需要排队
                    执行的这些线程对象所共享的。
             */
            //Object obj2 = new Object();
            //synchronized (this){
            //synchronized (obj) {
            //synchronized ("abc") { // "abc"在字符串常量池当中。
            //synchronized (null) { // 报错:空指针。
            //synchronized (obj2) { // 这样编写就不安全了。因为obj2不是共享对象。
            //synchronized(this){
                double before = this.getBalance();
                double after = before - money;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.setBalance(after);
    
        }
    }
    public class AccountThread extends Thread{
    
        // 两个线程必须共享同一个账户对象。
        private Account act;
    
        // 通过构造方法传递过来账户对象
        public AccountThread(Account act){
            this.act = act;
        }
    
        public Account getAct() {
            return act;
        }
    
        public void setAct(Account act) {
            this.act = act;
        }
    
        public void run(){
    
            //取款5000
            // run方法的执行表示取款操作。
            // 假设取款5000
            double money = 5000;
    
            // 取款
            // 多线程并发执行这个方法。
            //synchronized (this){//这里的this是AccountThread对象,这个对象不共享!
            synchronized (act) {//这种方式也可以,只不过扩大了同步的范围,效率更低了
                act.withdraw(money);
            }
            System.out.println(Thread.currentThread().getName() + "对" + act.getActno() + "取款" + money + "成功," + "账户余额为:" + act.getBalance());
        }
    }
    public class Test {
        public static void main(String[] args) {
    
            // 创建账户对象(只创建1个)
            Account act = new Account("act-no",10000);
    
            // 创建两个线程
            Thread t1 = new AccountThread(act);
            Thread t2 = new AccountThread(act);
            // 设置name
            t1.setName("t1");
            t2.setName("t2");
            // 启动线程取款
            t1.start();
            t2.start();
    
        }
    }
    
    • 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
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    运行结果:

    在这里插入图片描述

    示例代码03:
    /*
    银行账户
        不使用线程同步机制,多线程对同一个账户进行取款,出现线程安全问题。
     */
    public class Account implements Serializable{
        private static final long serialVersionUID = 1655286831056942086L;
        //账户
        private String actno;
        //余额
        private double balance;
    
        public Account() {
        }
    
        public Account(String actno, int balance) {
            this.actno = actno;
            this.balance = balance;
        }
    
        public String getActno() {
            return actno;
        }
    
        public void setActno(String actno) {
            this.actno = actno;
        }
    
        public double getBalance() {
            return balance;
        }
    
        public void setBalance(double balance) {
            this.balance = balance;
        }
    
        //取款方法
        /*
        在实例方法上可以使用synchronized吗?可以的。
            synchronized出现在实例方法上,一定锁的是this。
            没得挑。只能是this。不能是其他的对象了。
            所以这种方式不灵活。
    
            另外还有一个缺点:synchronized出现在实例方法上,
            表示整个方法体都需要同步,可能会无故扩大同步的
            范围,导致程序的执行效率降低。所以这种方式不常用。
    
            synchronized使用在实例方法上有什么优点?
                代码写的少了。节俭了。
    
            如果共享的对象就是this,并且需要同步的代码块是整个方法体,
            建议使用这种方式。
         */
        public synchronized void withdraw(double money){
    
           double before = this.getBalance();
            double after = before - money;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setBalance(after);
        }
    }
    public class AccountThread extends Thread{
    
        // 两个线程必须共享同一个账户对象。
        private Account act;
    
        // 通过构造方法传递过来账户对象
        public AccountThread(Account act){
            this.act = act;
        }
    
        public Account getAct() {
            return act;
        }
    
        public void setAct(Account act) {
            this.act = act;
        }
    
        public void run(){
    
            //取款5000
            // run方法的执行表示取款操作。
            // 假设取款5000
            double money = 5000;
    
            // 取款
            // 多线程并发执行这个方法。
            act.withdraw(money);
    
            System.out.println(Thread.currentThread().getName() + "对" + act.getActno() + "取款" + money + "成功," + "账户余额为:" + act.getBalance());
        }
    }
    public class Test {
        public static void main(String[] args) {
    
            // 创建账户对象(只创建1个)
            Account act = new Account("act-no",10000);
    
            // 创建两个线程
            Thread t1 = new AccountThread(act);
            Thread t2 = new AccountThread(act);
            // 设置name
            t1.setName("t1");
            t2.setName("t2");
            // 启动线程取款
            t1.start();
            t2.start();
    
        }
    }
    
    
    • 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
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    运行结果:

    在这里插入图片描述

    synchronized面试题01:

    doOther方法执行的时候需要等待doSome方法的结束吗?不需要

    因为doOther方法没有sychronized关键字,所以doOther方法不需要到锁池中获取锁

    示例代码04:
    public class Exam01 {
        public static void main(String[] args) {
            MyClass c = new MyClass();
    
            MyThread t1 = new MyThread(c);
            MyThread t2 = new MyThread(c);
    
            t1.setName("t1");
            t2.setName("t2");
            t1.start();
            try {
                Thread.sleep(1000);//这个睡眠的作用是:为了保证t1线程先执行。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            t2.start();
    
        }
    }
    
    class MyThread extends Thread {
        private MyClass mc;
    
        public MyThread(MyClass mc) {
            this.mc = mc;
        }
    
        public void run() {
    
            if(Thread.currentThread().getName().equals("t1")){
                mc.doSome();
            }
            if(Thread.currentThread().getName().equals("t2")){
                mc.doOther();
            }
        }
    }
    class MyClass {
    
            public synchronized void doSome() {
                System.out.println("doSome beign");
                try {
                    Thread.sleep(1000 * 5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("doSome over");
            }
    
            public void doOther() {
                System.out.println("doOther beign");
                System.out.println("doOther over");//因为doOther方法没有sychronized关键字,所以doOther方法不需要到锁池中获取锁
            }
        }
    
    • 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
    运行结果:

    在这里插入图片描述

    synchronized面试题02:

    doOther方法执行的时候需要等待doSome方法的结束吗?需要

    因为doOther方法有sychronized关键字,所以doOther方法需要排队(等待doSome方法执行玩释放锁之后)获取锁

    示例代码05:
    public class Exam02 {
            public static void main(String[] args) {
    
                MyClass1 a = new exam.MyClass1();
                MyThread1 t3 = new exam.MyThread1(a);
                MyThread1 t4 = new exam.MyThread1(a);
    
                t3.setName("t3");
                t4.setName("t4");
                t3.start();
                try {
                    Thread.sleep(1000);//这个睡眠的作用是:为了保证t1线程先执行。
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                t4.start();
    
            }
        }
    
    class MyThread1 extends Thread {
            private MyClass1 mc;
    
            public MyThread1(MyClass1 mc) {
                this.mc = mc;
            }
    
            public void run() {
    
                if(Thread.currentThread().getName().equals("t3")){
                    mc.doSome();
                }
                if(Thread.currentThread().getName().equals("t4")){
                    mc.doOther();
                }
            }
        }
    class MyClass1 {
            public synchronized void doSome() {
                System.out.println("doSome beign");
                try {
                    Thread.sleep(1000 * 5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("doSome over");
            }
    
            public synchronized void doOther() {
                System.out.println("doOther beign");
                System.out.println("doOther over");//因为doOther方法有sychronized关键字,所以doOther方法需要排队(等待doSome方法执行玩释放锁之后)获取锁
            }
        }
    
    • 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
    运行结果:

    在这里插入图片描述

    synchronized面试题03:

    doOther方法执行的时候需要等待doSome方法的结束吗?不需要

    因为MyClass对象是两个,两把锁

    示例代码06:
    public class Exam03 {
            public static void main(String[] args) {
                MyClass2 c1 = new MyClass2();
                MyClass2 c2 = new MyClass2();//创建两个对象就没有共享对象了,不需要排队获取锁
    
                MyThread2 t1 = new MyThread2(c1);
                MyThread2 t2 = new MyThread2(c2);
    
                t1.setName("t1");
                t2.setName("t2");
                t1.start();
                try {
                    Thread.sleep(1000);//这个睡眠的作用是:为了保证t1线程先执行。
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                t2.start();
    
            }
        }
    
        class MyThread2 extends Thread {
            private MyClass2 mc;
    
            public MyThread2(MyClass2 mc) {
                this.mc = mc;
            }
    
            public void run() {
    
                if(Thread.currentThread().getName().equals("t1")){
                    mc.doSome();
                }
                if(Thread.currentThread().getName().equals("t2")){
                    mc.doOther();
                }
            }
        }
        class MyClass2 {
    
            public synchronized void doSome() {
                System.out.println("doSome beign");
                try {
                    Thread.sleep(1000 * 5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("doSome over");
            }
    
            public synchronized void doOther() {
                System.out.println("doOther beign");
                System.out.println("doOther over");
            }
        }
    
    
    • 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
    运行结果:

    在这里插入图片描述

    synchronized面试题04:

    doOther方法执行的时候需要等待doSome方法的结束吗?需要

    因为静态方法是类锁,不管创建了几个对象,类锁只有

    示例代码07:
    public class Exam04 {
        public static void main(String[] args) {
    
            MyClass2 c = new MyClass2();
    
            MyThread2 t1 = new MyThread2(c);
            MyThread2 t2 = new MyThread2(c);
    
            t1.setName("t1");
            t2.setName("t2");
            t1.start();
            try {
                Thread.sleep(1000);//这个睡眠的作用是:为了保证t1线程先执行。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            t2.start();
    
        }
    }
    
    class MyThread3 extends Thread {
        private MyClass3 mc;
    
        public MyThread3(MyClass3 mc) {
            this.mc = mc;
        }
    
        public void run() {
    
            if(Thread.currentThread().getName().equals("t1")){
                mc.doSome();
            }
            if(Thread.currentThread().getName().equals("t2")){
                mc.doOther();
            }
        }
    }
    // synchronized出现在静态方法上是找类锁。
    class MyClass3 {
    
        public synchronized static void doSome() {
            System.out.println("doSome beign");
            try {
                Thread.sleep(1000 * 5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("doSome over");
        }
    
        public synchronized static void doOther() {
            System.out.println("doOther beign");
            System.out.println("doOther over");
        }
    }
    
    
    • 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
    运行结果:

    在这里插入图片描述

  • 相关阅读:
    docker目录挂载失败:Check if the specified host path exists and is the expected type
    现网工作中经常遇到却说不出来的技术名词,看看你都知道吗?-思科,华为,网络工程师
    用Java写PTA 7-7 较为复杂情况下的求和
    c++封装webrtc sdk(一):设计sdk基本结构
    vagrant+virtualbox的踩坑记录
    深入学习Android
    从尾到头打印链表 ,合并两个排序的链表、反转链表 :迭代法
    两种解法搞定Swap Nodes in Pairs算法题
    (三)Logistic回归的梯度下降
    什么是线程的拒绝策略&&核心线程数打满后就直接创建新线程吗
  • 原文地址:https://blog.csdn.net/qq_46096136/article/details/126806323