前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >11.线程八锁

11.线程八锁

作者头像
Devops海洋的渔夫
发布2022-03-23 16:25:00
2120
发布2022-03-23 16:25:00
举报
文章被收录于专栏:Devops专栏Devops专栏

11.线程八锁

线程八锁

代码语言:javascript
复制
• 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法

• 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法

• 加个普通方法后发现和同步锁无关

• 换成两个对象后,不是同一把锁了,情况立刻变化。

• 都换成静态同步方法后,情况又变化

• 所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。 

• 所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!

代码演示

1. 两个普通同步方法,两个线程,标准打印 数字,确认打印的顺序

1.1 首先写一个打印数字的方法类,两个方法,各自可以打印 one , two
代码语言:javascript
复制
//编写专门打印数字的方法类
class Number{

    //打印 one:注意设置同步方法 synchronized
    public synchronized void getOne(){
        // 打印 one
        System.out.println("one");
    }

    //打印 two:注意设置同步方法 synchronized
    public synchronized void getTwo(){
        //打印two
        System.out.println("two");
    }

}
1.2 创建两个线程,确认打印数字的顺序
代码语言:javascript
复制
public class TestThread8Monitor {

    public static void main(String[] args) {
        test01();
    }

    //1. 两个普通同步方法,两个线程,标准打印 数字,确认打印的顺序
    public static void test01(){
        //1.创建1个Number类对象,此时两个同步方法当前的锁为一个number对象
        Number number = new Number();

        //2.开启两个线程,因为当前两个线程的锁都是同一个number对象
        //  所以打印的信息,应该是 one two
        new Thread(new Runnable() {
            @Override
            public void run() {
                number.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                number.getTwo();
            }
        }).start();
    }

}

测试执行如下:

image-20201104084444121

2. 新增 Thread.sleep() 给 getOne(), 确认两个线程的打印顺序

2.1 在 getOne() 方法 设置 休眠

image-20201104121338613

测试执行如下:

image-20201104121421673

因为此时两个线程的锁都是同一个 number 对象,所以不管线程是否设置休眠,都是按照顺序同步执行的。

2.2 测试代码
代码语言:javascript
复制
public class TestThread8Monitor {

    public static void main(String[] args) {
        test01();
    }

    //1. 两个普通同步方法,两个线程,标准打印 数字,确认打印的顺序
    public static void test01(){
        //1.创建1个Number类对象,此时两个同步方法当前的锁为一个number对象
        Number number = new Number();

        //2.开启两个线程,因为当前两个线程的锁都是同一个number对象
        //  所以打印的信息,应该是 one two
        new Thread(new Runnable() {
            @Override
            public void run() {
                number.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                number.getTwo();
            }
        }).start();
    }

}

//编写专门打印数字的方法类
class Number{

    //打印 one:注意设置同步方法 synchronized
    public synchronized void getOne(){

        // 休眠3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印 one
        System.out.println("one");
    }

    //打印 two:注意设置同步方法 synchronized
    public synchronized void getTwo(){
        System.out.println("two");
    }

}

3. 新增普通方法 getThree(), 并且建立线程。查看打印顺序

3.1 创建 getThree() 的普通方法,注意不是同步方法,也就是说跟 getOne() getTwo() 不是共用一个锁。

image-20201104121759406

代码语言:javascript
复制
//编写专门打印数字的方法类
class Number{

    //打印 one:注意设置同步方法 synchronized
    public synchronized void getOne(){

        // 休眠3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印 one
        System.out.println("one");
    }

    //打印 two:注意设置同步方法 synchronized
    public synchronized void getTwo(){
        System.out.println("two");
    }

    //打印 three。普通方法,没有设置同步锁
    public void getThree(){
        System.out.println("three");
    }
}
3.2 在测试代码增加执行 getThree() 的线程,查看打印的顺序

image-20201104121956905

代码语言:javascript
复制
//1.创建1个Number类对象,此时两个同步方法当前的锁为一个number对象
Number number = new Number();

//2.开启两个线程,因为当前两个线程的锁都是同一个number对象
//  所以打印的信息,应该是 one two
new Thread(new Runnable() {
    @Override
    public void run() {
        number.getOne();
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        number.getTwo();
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        number.getThree(); // 因为没有同步锁,所以会优先并发打印【因为getOne需要修改3秒】
    }
}).start();
3.3 分析

“首先 getThree 没有同步锁,所以是并发执行的。 而 getOne 应该也是并发执行的,但是需要休眠3秒后才 打印。 而 getTwo 因为与 getOne 共用一个同步锁 number 对象,所以需要等待 getOne 执行完毕之后,才能执行 getTwo ”

4. 两个普通同步方法,两个 Number 对象 ,查看打印顺序

4.1 保持 getOne 与 getTwo 两个同步方法
代码语言:javascript
复制
//编写专门打印数字的方法类
class Number{

    //打印 one:注意设置同步方法 synchronized
    public synchronized void getOne(){

        // 休眠3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印 one
        System.out.println("one");
    }

    //打印 two:注意设置同步方法 synchronized
    public synchronized void getTwo(){
        System.out.println("two");
    }

}
4.2 创建两个 Number 对象,分别用不同的 Number 对象调用 getOne 和 getTwo 执行线程, 查看打印的顺序

image-20201104122909591

代码语言:javascript
复制
//1.创建2个Number类对象,
// 此时两个同步方法当前的锁为各自的number1和number2对象,并不共用锁,也就是可以并发
Number number1 = new Number();
Number number2 = new Number();

//2.开启两个线程
//  此时两个同步方法当前的锁为各自的number1和number2对象,并不共用锁,也就是可以并发
//  虽然并发,但是getOne需要休眠3秒,所以优先打印getTwo,结果:two one
new Thread(new Runnable() {
    @Override
    public void run() {
        number1.getOne();
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        number2.getTwo();
    }
}).start();

5. 修改 getOne() 为静态同步方法,保持 getTwo() 为普通的同步方法,查看打印的顺序

5.1 修改 getOne 为静态同步方法
代码语言:javascript
复制
class Number{

    //打印 one:注意设置同步方法 synchronized
    public static synchronized void getOne(){

        // 休眠3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印 one
        System.out.println("one");
    }

    //打印 two:注意设置同步方法 synchronized
    public synchronized void getTwo(){
        System.out.println("two");
    }

}
5.3 测试方法

image-20201104123706997

代码语言:javascript
复制
//1.创建1个Number类对象,
Number number = new Number();

//2.开启两个线程
//  此时getOne的同步锁是 Number.class,而getTwo的同步锁是 number 对象
//  也就是说两个线程不是共用一个同步锁,所以两个线程可以并发
//  其中getOne需要休眠3秒,才打印信息。所以打印结果: two one
new Thread(new Runnable() {
    @Override
    public void run() {
        Number.getOne();
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        number.getTwo();
    }
}).start();

6. 修改两个方法均为静态同步方法,一个 Number 对象

6.1 设置两个方法为静态同步方法
代码语言:javascript
复制
//编写专门打印数字的方法类
class Number{

    //打印 one:注意设置同步方法 synchronized
    public static synchronized void getOne(){

        // 休眠3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印 one
        System.out.println("one");
    }

    //打印 two:注意设置同步方法 synchronized
    public static synchronized void getTwo(){
        System.out.println("two");
    }
}
6.2 测试方法

image-20201104124108564

代码语言:javascript
复制
//1.创建1个Number类对象,
Number number = new Number();

//2.开启两个线程
//  此时getOne 和 getTwo的同步锁是 number 对象的 Class
//  也就是说两个线程共用一个同步锁,所以两个线程串行同步执行
//  所以打印结果:  one two
new Thread(new Runnable() {
    @Override
    public void run() {
        number.getOne();
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        number.getTwo();
    }
}).start();

7.一个静态同步方法,一个非静态同步方法,两个 Number 对象

7.1 修改getOne 和 getTwo 方法
代码语言:javascript
复制
//编写专门打印数字的方法类
class Number{

    //打印 one:注意设置同步方法 synchronized
    public static synchronized void getOne(){

        // 休眠3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印 one
        System.out.println("one");
    }

    //打印 two:注意设置同步方法 synchronized
    public synchronized void getTwo(){
        System.out.println("two");
    }

}
7.2 测试方法

image-20201104125101349

代码语言:javascript
复制
//1.创建2个Number类对象,
Number number1 = new Number();
Number number2 = new Number();

//2.开启两个线程
//  此时getOne 和 getTwo的同步锁是不同的,各自是number1和number2对象
//  所以两个线程可以并发执行。由于getOne需要休眠3秒,
//  所以打印结果:   two one
new Thread(new Runnable() {
    @Override
    public void run() {
        number1.getOne();
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        number2.getTwo();
    }
}).start();

8.两个静态同步方法,两个 Number 对象

8.1 设置两个静态同步方法
代码语言:javascript
复制
//编写专门打印数字的方法类
class Number{

    //打印 one:注意设置同步方法 synchronized
    public static synchronized void getOne(){

        // 休眠3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印 one
        System.out.println("one");
    }

    //打印 two:注意设置同步方法 synchronized
    public static synchronized void getTwo(){
        System.out.println("two");
    }

}
8.2 测试方法

image-20201104125513693

代码语言:javascript
复制
//1.创建2个Number类对象,
Number number1 = new Number();
Number number2 = new Number();

//2.开启两个线程
//  此时getOne 和 getTwo的同步锁相同的,都是 Number.class
//  所以两个线程同步串行执行。
//  所以打印结果: one two
new Thread(new Runnable() {
    @Override
    public void run() {
        number1.getOne();
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        number2.getTwo();
    }
}).start();

总结

代码语言:javascript
复制
/*
* 线程八锁的关键:
* ①非静态方法的锁默认为  this,  静态方法的锁为 对应的 Class 实例
* ②某一个时刻内,只能有一个线程持有锁,无论几个方法。
*/
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 海洋的渔夫 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 11.线程八锁
    • 线程八锁
      • 代码演示
        • 1. 两个普通同步方法,两个线程,标准打印 数字,确认打印的顺序
        • 2. 新增 Thread.sleep() 给 getOne(), 确认两个线程的打印顺序
        • 3. 新增普通方法 getThree(), 并且建立线程。查看打印顺序
        • 4. 两个普通同步方法,两个 Number 对象 ,查看打印顺序
        • 5. 修改 getOne() 为静态同步方法,保持 getTwo() 为普通的同步方法,查看打印的顺序
        • 6. 修改两个方法均为静态同步方法,一个 Number 对象
        • 7.一个静态同步方法,一个非静态同步方法,两个 Number 对象
        • 8.两个静态同步方法,两个 Number 对象
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档