当前位置: 首页 > news >正文

多线程synchronized——线程“八锁”

一、锁对象的概念

  • 非静态同步方法锁的是当前实例对象(this)。当一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁。
  • 静态同步方法锁的是当前类的Class对象。当一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,这种互斥行为适用于同一个类的所有实例对象。
  • 修饰代码块:锁的是括号中的对象。
synchronized(对象){

}

二、线程“八锁”

  • 所谓的线程八锁:其实就是从八段代码例子中,理解synchronized的用法,不要太纠结这个名称,不需要纠结八种情况里面好像有些情况是相似的,只需要理解这八个代码例子里面的synchronized 的用法,其他都不用管。(其实就是考察 synchronized 锁住的是哪个对象)
    • 如果纠结这八种情况是否有重复或者是相似的事情,就像我一样掉坑里了,就比如情况一和情况二,它两的区别就是情况二多了一个sleep方法,我当时就一直想,这两种情况不都是一样的嘛,为啥是两种情况呢?唉,他喵的,后来我就悟了,我他喵的想那么多干嘛,我理解它每个情况synchronized 的用法就行了呀。

1情况一:两个线程分别访问 同一个对象(实例对象) 的两个普通同步方法

  • synchronized修饰普通方法默认使用的锁对象是this,由于是同一个对象的两个方法,所以他们的锁对象也是同一个,因此这两个方法会互斥执行。当一个线程执行其中一个方法时,另一个线程必须等待。
    • 因为这里使用的是用一个Number对象n1,所以线程1和线程2锁住的对象都是this(n1),它们是互斥的。
public class Tongbu {

    public static void main(String[] args) {
        Number n1 = new Number();
//        当启动多个线程时,线程的执行顺序是不确定的,这主要取决于操作系统的线程调度器。
        new Thread(()->{ n1.a();}).start();
        new Thread(()->{ n1.b(); }).start();
    }

}

class Number{
    public synchronized void a() {
        System.out.println("a()");
    }
    public synchronized void b() {
        System.out.println("b()");
    }
}
  • 可能出现的结果:
结果1:(先执行第一个线程)
a()
b()

结果2:(先执行第二个线程)
b()
a()

情况二:两个线程分别访问 同一个对象(实例对象) 的两个普通同步方法,与情况一相比就多了一个sleep方法

  • synchronized修饰普通方法默认使用的锁对象是this,由于是同一个对象的两个方法,所以他们的锁对象也是同一个,因此这两个方法会互斥执行。当一个线程执行其中一个方法时,另一个线程必须等待。
    • 因为这里使用的是用一个Number对象n1,所以线程1和线程2锁住的对象都是this(n1),它们是互斥的。(sleep方法不会释放锁的
public class Tongbu {

    public static void main(String[] args) {
        Number n1 = new Number();
//        当启动多个线程时,线程的执行顺序是不确定的,这主要取决于操作系统的线程调度器。
        new Thread(()->{ n1.a();}).start();
        new Thread(()->{ n1.b(); }).start();
    }

}

class Number{
    public synchronized void a() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("a()");
    }
    public synchronized void b() {
        System.out.println("b()");
    }
}
  • 可能出现的结果:
结果1:(先执行第一个线程)
-- 等待1s然后输出
a()
b()

结果2:(先执行第二个线程)
b()
-- 等待1s然后输出
a()

情况三:在情况二的基础上新增了一个没有被synchronized修饰的普通方法,再启动一个新的线程执行新增的方法

  • synchronized修饰普通方法默认使用的锁对象是this,由于是同一个对象的两个方法,所以他们的锁对象也是同一个,因此这两个方法会互斥执行。当一个线程执行其中一个方法时,另一个线程必须等待。
    • 因为这里使用的是用一个Number对象n1,所以线程1和线程2锁住的对象都是this(n1),它们是互斥的。(sleep方法不会释放锁的),但是c方法没有synchronized 修饰,所以它没有锁,所以c方法启动的时候不会有互斥效果,它的执行不会受到a或b方法执行的影响。
public class Tongbu {

    public static void main(String[] args) {
		Number n1 = new Number();
//        当启动多个线程时,线程的执行顺序是不确定的,这主要取决于操作系统的线程调度器。
        new Thread(()->{ n1.a();}).start();
        new Thread(()->{ n1.b(); }).start();
        new Thread(()->{ n1.c(); }).start();
    }

}

class Number{
    public synchronized void a() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("a()");
    }
    public synchronized void b() {
        System.out.println("b()");
    }
    public void c() {
        System.out.println("c()");
    }
}

  • 可能出现的结果:
结果1:
c()
-- 等待1s后输出
a()
b()

结果2:
c()
b()
-- 等待1s后输出
a()

结果3:
b()
c()
-- 等待1s后输出
a()

2情况四:两个线程分别访问两个不同对象的两个普通同步方法:

  • 由于synchronized修饰普通方法默认使用的锁对象是this,由于是两个实例对象,所以它们使用的锁也不同(两个不同的对象,它们的this不一样)。因此,这两个方法可以同时被不同的线程访问,不会互斥
    • 这里的n1和n2不是一个对象实例,所以它俩不互斥。线程1锁住的对象是n1,线程2锁住的对象是n2,这两个线程是并行执行的。
public class Tongbu {

    public static void main(String[] args) {
//        当启动多个线程时,线程的执行顺序是不确定的,这主要取决于操作系统的线程调度器。
         Number n1 = new Number();
         Number n2 = new Number();
        new Thread(()->{ n1.a();}).start();
        new Thread(()->{ n2.b(); }).start();
    }

}

class Number{
    public synchronized void a() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         System.out.println("a()");
    }

    public synchronized void b() {
        System.out.println("b()");
    }
}
  • 可能出现的结果:
结果1:(即线程2总是先执行完(因为线程1要等待1s))
b()
-- 等待1s后输出
a()

3情况五:一个线程访问访问类的静态同步方法,另一个线程对象的普通同步方法:

  • 由于锁对象不同(一个是类的Class对象,另一个是实例对象this),因此这两个方法不会互斥执行。它们可以同时被不同的线程访问,即它们是同时执行的
public class Tongbu {

    public static void main(String[] args) {
//        当启动多个线程时,线程的执行顺序是不确定的,这主要取决于操作系统的线程调度器。
         Number n = new Number();
        new Thread(()->{ n.a();}).start();
        new Thread(()->{ n.b(); }).start();
    }

}

class Number{
    public  static  synchronized void a() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         System.out.println("a()");
    }
    
    public synchronized void b() {
        System.out.println("b()");
    }
}
  • 可能出现的结果:
结果1:
b()
-- 等待1s后输出
a()

4情况六:两个线程分别访问同一个类的两个静态同步方法:

  • 由于静态方法默认使用的锁对象是类的Class对象,所以两个线程获取的锁对象都是同一个,因此这两个方法会互斥执行。当一个线程执行其中一个静态方法时,另一个线程必须等待。
public class Tongbu {

    public static void main(String[] args) {
//        当启动多个线程时,线程的执行顺序是不确定的,这主要取决于操作系统的线程调度器。
        new Thread(()->{ Number.a();}).start();
        new Thread(()->{ Number.b(); }).start();
    }

}

class Number{
    public static synchronized void a() {
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("a()");
    }
    public static synchronized void b() {
        System.out.println("b()");
    }
}

  • 可能出现的结果:
结果1:先执行第一个线程
-- 等待1s后输出
a()
b()

结果2:先执行第二个线程
b()
-- 等待1s后输出
a()

情况七:两个线程分别访问两个不同对象的静态方法和普通方法:

  • 由于锁对象不同(一个是类的Class对象,另一个是实例对象this),因此这两个方法不会互斥执行。它们可以同时被不同的线程访问,即它们是同时执行的
    • 虽然线程1是使用实例对象调用静态方法a,但是它锁的对象还是Number类.class对象,线程2锁的对象是n2对象(this),所以它们锁的不是同一个对象,所以它们不互斥。
public class Tongbu {

    public static void main(String[] args) {
//        当启动多个线程时,线程的执行顺序是不确定的,这主要取决于操作系统的线程调度器。
         Number n1 = new Number();
          Number n2 = new Number();
        new Thread(()->{ n1.a();}).start();
        new Thread(()->{ n2.b(); }).start();
    }

}

class Number{
    public  static  synchronized void a() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         System.out.println("a()");
    }
    
    public synchronized void b() {
        System.out.println("b()");
    }
}
  • 可能出现的结果:
结果1:
b()
-- 等待1s后输出
a()

情况八:两个线程分别访问两个不同对象的两个静态同步方法:

  • 由于静态方法默认使用的锁对象是类的Class对象,所以两个线程获取的锁对象都是同一个,因此这两个方法会互斥执行。当一个线程执行其中一个静态方法时,另一个线程必须等待。
    • 虽然线程1和线程2调用静态a、b方法是两个不同的实例对象,但是它们锁的都是Number.class对象,所以它们锁的是同一个对象,所以它们是互斥的。
public class Tongbu {

    public static void main(String[] args) {
//        当启动多个线程时,线程的执行顺序是不确定的,这主要取决于操作系统的线程调度器。
         Number n1 = new Number();
         Number n2 = new Number();
        new Thread(()->{ n1.a();}).start();
        new Thread(()->{ n2.b(); }).start();
    }

}

class Number{
     public static synchronized void a() {
       
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("a()");
    }
    public static synchronized void b() {
        System.out.println("b()");
    }
}
  • 可能出现的结果:
结果1:(先执行第一个线程)
a()
b()

结果2:(先执行第二个线程)
b()
a()

正常来讲,静态方法是有类对象调用的,而不是由类的实例对象调用的(在面向对象编程中,静态方法(static method)是属于类本身的,而不是属于类的某个实例对象。)

  • Java语法允许通过实例对象调用静态方法,这不会引发编译错误或运行时错误。
  • 实例对象调用静态方法时,不会隐式地传递实例对象(即 this 引用),静态方法内无法直接访问实例属性或实例方法(除非通过其他方式获取实例对象的引用)。

相关文章:

  • 数据通信学习笔记之OSPF其他内容1
  • 精益架构设计:深入理解与实践 C# 中的单一职责原则
  • 单播、广播、组播和任播
  • 浔川社团官方联合会维权成功
  • 单一职责原则开闭原则其他开发原则
  • 【HarmonyOS Next之旅】DevEco Studio使用指南(五) -> 添加/删除Module
  • MySql创建分区表并且按月分区
  • 数据库:一文掌握 Elasticsearch 的各种指令(Elasticsearch指令备忘)
  • 用户态内核态切换
  • 什么?获取到了未知的复位原因?
  • SOFABoot-07-版本查看
  • Linux系统管理与编程08:任务驱动综合应用
  • SAP SD学习笔记33 - 预詑品(寄售物料),预詑品引渡(KB),预詑品出库(KE)
  • S32k3XX MCU时钟配置
  • Gone v2 使用 goner/viper/remote链接远程的配置中心,支持etcd、consul、firestore、nats
  • 【Linux 下的 bash 无法正常解析, Windows 的 CRLF 换行符问题导致的】
  • 记忆力训练day24
  • C语言入门教程100讲(3)代码注释
  • 【免费】2000-2019年各省地方财政印花税数据
  • k8s--集群内的pod调用集群外的服务
  • “80后”北京市东城区副区长王智勇获公示拟任区委常委
  • 冰雹造成车损能赔吗?如何理赔?机构答疑
  • 国台办:民进党当局刻意刁难大陆配偶,这是不折不扣的政治迫害
  • 网信部门曝光网络谣言典型案例,“AI预测彩票号码百分百中奖”等在列
  • 4月国产新能源,降价潮迈入拐点
  • 陈宝良 高寿仙 彭勇︱明清社会的皇权、商帮与市井百态