自学内容网 自学内容网

Java中的 锁现象演示和原理解释 多线程操作资源类 八种案例 同步方法 静态方法 对象锁 类锁

目录

面试题

案例 1 标准访问有 ab 两个线程

案例 2 其中一个同步方法暂停 3 秒

案例 3 新增普通方法

案例 4 创建两个对象

案例 5 两个静态同步方法 一个对象

案例 6 两个静态同步方法 两个对象

案例 7 一个静态同步方法 一个普通同步方法 一个对象

案例 8 一个静态同步方法 一个普通同步方法 两个对象

思考总结

notify 方法


面试题

案例 1 标准访问有 ab 两个线程

在主线程里面暂停几秒钟的线程

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public synchronized void sendEmail(){
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
}
public class LockDemo1 {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.sendSMS();
        },"b").start();

    }
}

打印输出

案例 2 其中一个同步方法暂停 3 秒

在资源类的方法里面暂停几秒钟

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
}
public class LockDemo1 {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.sendSMS();
        },"b").start();

    }
}

sendEmail()sendSMS() 都是使用 synchronized 修饰的,这意味着它们在同一时刻只能有一个线程能进入执行。加锁是基于 phone 类的实例的,也就是说,如果有一个线程在执行某个同步方法,其他线程就会被阻塞,直到该线程执行完成并释放锁。

  • 当第一个线程(线程 a)执行 phone.sendEmail() 方法时,它会获取 phone 对象的锁,并且由于该方法包含 TimeUnit.SECONDS.sleep(2),它会在执行时暂停 2 秒。
  • sendEmail 方法执行期间,第二个线程(线程 b)尝试执行 phone.sendSMS(),但是由于 sendSMS 也是一个同步方法,且它也是基于 phone 对象的锁,所以它会被阻塞,直到线程 a 执行完成并释放锁。

案例 3 新增普通方法

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.hello();
            phone.sendSMS();
        },"b").start();

    }
}

案例 4 创建两个对象

对象锁

一个对象一把锁

两个对象两把锁

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类
        phone phone2 = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
//            phone.hello();
//            phone.sendSMS();
            phone2.sendSMS();
        },"b").start();

    }
}

案例 5 两个静态同步方法 一个对象

静态关键字

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public static synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public static synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.sendSMS();
        },"b").start();

    }
}

当你在 sendEmail()sendSMS() 方法上添加了 static synchronized 关键字后,锁的行为发生了变化。原来这些方法是基于实例对象 (phone 类的实例) 的锁,而加了 static 之后,这些方法变成了基于类锁(类对象锁)。

关键点:

  1. 非静态同步方法:如果没有 static,同步方法会使用实例对象的锁(即 phone 类的实例锁),每个对象都有自己的锁。
    • 在这种情况下,两个线程在同一个 phone 实例上调用 sendEmail()sendSMS() 时,锁是独立的,因为每个对象的锁是分开的。
  1. 静态同步方法:当你使用 static synchronized 时,锁就变成了类级别的锁。这意味着所有类的实例(无论创建多少个实例)都会共享同一把锁,而这把锁是与 phone 类相关的,而不是某个具体实例对象。
    • 也就是说,sendEmail()sendSMS() 不再是基于单个对象 phone 的锁,而是基于 phone 类的类锁。

对你的代码的影响:

  • 静态同步方法sendEmail()sendSMS() 都是静态同步方法,这意味着它们都使用 phone.class 作为锁。
    • 当线程 a 执行 phone.sendEmail() 时,它会获取 phone.class 锁,阻塞其他线程执行任何静态同步方法,直到 sendEmail() 完成。
    • 当线程 b 执行 phone.sendSMS() 时,它也需要获取 phone.class 锁,但因为线程 a 正在执行 sendEmail(),它会被阻塞直到 sendEmail() 执行完成。
  • 非静态方法hello() 方法没有 synchronizedstatic,因此它不是同步的,线程可以自由地调用它,且不会与静态同步方法互相影响。

锁的流程:

  1. 线程 a 调用 sendEmail(),获取 phone.class 锁,开始执行,执行期间睡眠 3 秒。
  2. 线程 b 调用 sendSMS(),但是由于 sendEmail() 还在执行,sendSMS() 会被阻塞,直到线程 a 执行完 sendEmail() 并释放 phone.class 锁。

总结:

当你在方法上加了 static synchronized 关键字时,锁是基于类锁(phone.class),而不是基于对象锁(phone 实例)。这意味着所有线程都必须获取类级别的锁才能进入这两个方法。所以,即使你创建了多个 phone 类的实例,静态同步方法依然会使用同一个类锁。

案例 6 两个静态同步方法 两个对象

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public static synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public static synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类
        phone phone2 = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.sendSMS();
        },"b").start();

    }
}

案例 7 一个静态同步方法 一个普通同步方法 一个对象

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public static synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类
        phone phone2 = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.sendSMS();
        },"b").start();

    }
}

案例 8 一个静态同步方法 一个普通同步方法 两个对象

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public static synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类
        phone phone2 = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.sendSMS();
        },"b").start();

    }
}

思考总结

我们要明白 sync 锁定的是什么

阿里开发手册

能用对象锁 就不要用类锁

synchronized 不是锁方法

是锁资源类

同步方法资源类是对象

同步静态方法资源类是当前类

同步方法锁的是当前对象

同一时间只能允许一个 线程进来同步方法

不允许同时

普通的同步方法锁的是对象锁 对象.class

静态的同步方法锁的是模版 类锁 类.class

能用对象锁 就不要用类锁

notify 方法


原文地址:https://blog.csdn.net/qq_30500575/article/details/145233584

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!