synchronized的8锁问题(区分默认用的是那把锁) 笔记云备份

需要提前了解的概念

1.1 并行和并发

并发(Concurrency)

核心:同一段时间内"有很多任务在推进",但不一定真的同时做。 通常表现为:大家轮流用同一份资源,谁拿到谁先用。

多线程/多任务"争用同一个资源"是并发里常见的一种情况,但并发不只等于"抢资源"。

例子: 春运抢票 电商秒杀

并发 = 看起来同时发生,其实可能是轮流推进。

并行(Parallelism)

核心:多个任务真的在同一时刻一起执行。 一般需要"多个 CPU 核心/多个工人"同时干活。

例子:

更硬核一点:两个人做饭,一个切菜一个炒菜(真正并行)

并行 = 真同时干活。

1.2 同步和异步

同步(Synchronous):

一件事做完,才能做下一件事。

像排队点餐:前一个人没点完,你就得等着。

异步(Asynchronous):

先把事"交代出去",自己继续干别的;等结果好了再来处理。

像你发消息:发完你就去刷别的,不用等对方回。

开新线程通常能让任务不阻塞当前流程,所以属于常见的异步/并发执行方式。

1.3线程的状态

  1. 新建(New):线程被创建但尚未启动执行。
  2. 就绪(Runnable):线程等待CPU时间片以便执行,也就是处于就绪状态。
  3. 阻塞(Blocked):线程暂停执行,通常是因为等待某些条件满足,例如等待I/O操作完成、等待锁释放等。
  4. 无限期等待(Waiting):线程无限期地等待某个条件的发生,通常需要其他线程来唤醒它。
  5. 有限期等待(Timed Waiting):线程等待一段时间,超过指定时间后会自动唤醒。
  6. 终止(Terminated):线程执行完成或者异常终止,进入终止状态。

Thead类中的源码里可以看到线程的以下状态:

java 复制代码
public enum State {
    /**
     * Thread state for a thread which has not yet started.
     */
    NEW, //新建

    /**
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */
    RUNNABLE, //就绪

    /**
     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock
     * to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
    BLOCKED, //阻塞

    /**
     * Thread state for a waiting thread.
     * A thread is in the waiting state due to calling one of the
     * following methods:
     * <ul>
     *   <li>{@link Object#wait() Object.wait} with no timeout</li>
     *   <li>{@link #join() Thread.join} with no timeout</li>
     *   <li>{@link LockSupport#park() LockSupport.park}</li>
     * </ul>
     *
     * <p>A thread in the waiting state is waiting for another thread to
     * perform a particular action.
     *
     * For example, a thread that has called {@code Object.wait()}
     * on an object is waiting for another thread to call
     * {@code Object.notify()} or {@code Object.notifyAll()} on
     * that object. A thread that has called {@code Thread.join()}
     * is waiting for a specified thread to terminate.
     */
    WAITING, //不见不散

    /**
     * Thread state for a waiting thread with a specified waiting time.
     * A thread is in the timed waiting state due to calling one of
     * the following methods with a specified positive waiting time:
     * <ul>
     *   <li>{@link #sleep Thread.sleep}</li>
     *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
     *   <li>{@link #join(long) Thread.join} with timeout</li>
     *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
     *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
     * </ul>
     */
    TIMED_WAITING, //过时不候

    /**
     * Thread state for a terminated thread.
     * The thread has completed execution.
     */
    TERMINATED; //终止
}

2 synchronized 入门

如何编写企业需要的工程化多线程代码?

多线程编程模板:

  • 线程 操作 资源类

实现步骤:

  1. 创建资源类
  2. 资源类里创建同步方法、同步代码块
  3. 多线程调用

例子: 卖票的程序 , 多个窗口同时买票, 用多线程进行模拟.

java 复制代码
//资源类
class Ticket{
    int num =20;
    public void sell(){
        if(num<=0){
            System.out.println("卖没了");
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"卖完之后还剩: "+num);
    }
}

// 多线程模版公式 。线程操作资源类
public class SellTacket {
    public static void main(String[] args) {
        Ticket ticket=new Ticket();

        new Thread(()->{
            for (int i = 0; i <= 20; i++) {
                ticket.sell();

            }
        },"窗口1").start();

        new Thread(()->{
            for (int i = 0; i <= 20; i++) {
                ticket.sell();

            }
        },"窗口2").start();

    }
}
  • 修改方法

2.1 synchronized的8锁问题(区分默认用的是那把锁)

synchronized锁的是什么?

看下面这段儿代码,回答多线程的8个问题:

  1. 先访问短信,再访问邮件,先打印短信还是邮件
  2. 停4秒在短信方法内,先打印短信还是邮件
  3. 先访问短信,再访问hello方法,是先打短信还是hello
  4. 现在有两部手机,第一部发短信,第二部发邮件,先打印短信还是邮件
  5. 两个静态同步方法,1部手机,先打印短信还是邮件
  6. 两个静态同步方法,2部手机,先打印短信还是邮件
  7. 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
  8. 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
java 复制代码
class Phone {

    public synchronized void sendSMS() {
        //TimeUnit.SECONDS.sleep(4);
        System.out.println("sendSMS");
    }
    

    public synchronized void sendEmail() {
        System.out.println("sendEmail");
    }

    public void hello() {
        System.out.println("hello");
    }

}

public class Lock8 {
    
    public static void main(String[] args) {

        //  线程操作资源类
        Phone phone = new Phone();

        //  调用短信
        new Thread(()->{
            phone.sendSMS();
        }).start();

        //  调用邮件
        new Thread(()->{
            phone.sendEmail();
        }).start();
    }
}

下面例子的答案都是基于: CPU的调度是随机的, 但是那个线程先启动,那个线程就有先天优势, 这里先不考虑随机的情况, 就看谁先启动.

  • 成员方法依赖对象存在, 静态方法依赖于类存在 , 对应的锁也是一样
  1. 先访问短信,再访问邮件,先打印短信还是邮件?

    一旦进入了sendSMS方法, sendEmail方法只能等待锁释放. 相反也是

    两个方法都是属于同步方法:正常按照顺序执行. 同时这个两个方法使用的锁是同一把锁!

  2. 停4秒在短信方法内,先打印短信还是邮件?

    虽然睡觉,但是锁不释放.

    原因:短信与邮件使用的是同一把锁 ,那么这个锁是谁? this 也就是phone对象, 谁调用方法这个this就是谁

  3. 先访问短信,再访问hello方法,是先打短信还是hello

    先hello ,再短信!

    原因:一个有锁,一个没有锁!然后按照顺序执行, 短信中有睡眠,hello 中没有,先执行hello!

  1. 现在有两部手机,第一部发短信,第二部发邮件,先打印短信还是邮件?
    先邮件,再短信!
    原因:两个同步方法使用的不是同一把锁:因为对象变了 ,phone ,phone2
  1. 两个静态同步方法,1部手机,先打印短信还是邮件

    先短信,再邮件(以代码先天顺序优势的情况下看)

    因为同一把锁,那么这个锁是谁? Phone.class

    静态方法随着类的加载而加载,此时没有对象!

  1. 两个静态同步方法,2部手机,先打印短信还是邮件
    先短信,再邮件:
    Phone phone = new Phone(); Phone phone2 = new Phone();
    原因:同一把锁! phone and phone2 都是来自于同一个元模板: Phone.class, 静态的同步方法, 与实例对象有几个没有关系.
  1. 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
    先邮件,再短信
    原因:短信锁,Phone.class 邮件锁,this
  1. 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
    先邮件,再短信
    原因:短信锁Phone.class 邮件锁this
总结

synchronized = 给共享资源加互斥锁,让多线程排队进入临界区。

Java中的每一个对象都可以作为锁。具体表现为以下3种形式:

  1. 对于普通同步方法,锁是当前实例对象
  2. 对于静态同步方法,锁是当前类的Class对象
  3. 对于同步代码块,锁是Synchonized括号里配置的对象
  4. 而静态同步方法(Class对象锁)与非静态同步方法(实例对象锁)之间是不会有竞争的。

sleep()wait()的 区别

最核心的区别在于:sleep 是"带着锁睡觉",而 wait 是"空手睡觉"

特性 Thread.sleep() Object.wait()
所属类 属于 Thread 类的静态方法 属于 Object 类的成员方法
锁的状态 (最重要) 不释放锁。线程睡着了也占着资源。 释放锁。让出资源让其他线程进入。
使用范围 可以在任何地方使用 必须在 synchronized 代码块中调用
唤醒条件 时间到自动苏醒 notify() / notifyAll() 唤醒
相关推荐
sino爱学习2 小时前
别再踩 Stream 的坑了!Java 函数式编程安全指南
java·后端
Sunsets_Red2 小时前
2025 FZYZ夏令营游记
java·c语言·c++·python·算法·c#
自由生长20243 小时前
从流式系统中思考-C++生态和Java生态的区别
java·c++
培培说证3 小时前
2026大专Java开发工程师,考什么证加分?
java·开发语言·python
qq_336313933 小时前
java基础-方法引用
java·开发语言·算法
总是学不会.3 小时前
【JUC编程】一、线程的基础概念
java·开发语言·jvm
由之3 小时前
Spring事件监听机制简单使用
java·spring
小鸡吃米…3 小时前
Python - 类属性
java·前端·python
沉下去,苦磨练!3 小时前
计算一个字符串在另一个字符串中出现次数
java·开发语言