【JAVA】synchronized 关键字的底层实现涉及得三个队列

synchronized 关键字的底层实现确实涉及三个主要的队列:cxq(Contention Queue)、EntryList 和 waitSet。这三个队列分别用于管理不同类型的等待线程。下面是每个队列的详细解释及其作用:

1. cxq(Contention Queue)

定义:cxq 是一个竞争队列,用于存储那些未能立即获得锁的线程。
行为:当一个线程试图获取一个已经被其他线程持有的锁时,该线程会被放入 cxq 中等待。cxq 是一个基于链表的数据结构,线程按照先进先出(FIFO)的原则排队。当锁被释放时,cxq 中的一个线程会被移到 EntryList 中,准备重新竞争锁。

2. EntryList

定义:EntryList 是一个等待队列,用于存储那些已经准备好竞争锁的线程。
行为:当 cxq 中的线程被移到 EntryList 中时,它们会尝试获取锁。
EntryList 也是一个基于链表的数据结构,线程按照先进先出(FIFO)的原则排队。当锁被释放时,EntryList 中的一个线程会被选中并获得锁。

3. waitSet

定义:waitSet 是一个条件等待队列,用于存储那些调用 wait 方法后被阻塞的线程。
行为
当一个线程在临界区内调用 wait 方法时,它会释放锁并被放入 waitSet 中等待。
当其他线程调用 notify 方法时,waitSet 中的一个线程会被唤醒并重新竞争锁。
当其他线程调用 notifyAll 方法时,waitSet 中的所有线程都会被唤醒并重新竞争锁。

详细流程

获取锁:
线程 A 尝试进入由 synchronized 保护的代码块。
如果锁可用,线程 A 获得锁并进入临界区。
如果锁不可用,线程 A 被放入 cxq 中等待。
进入临界区:
线程 A 获得锁并进入临界区,执行受保护的代码。
调用 wait 方法:
线程 A 在临界区内调用 wait 方法。
线程 A 释放锁并被放入 waitSet 中等待。
其他线程(例如线程 B)可以竞争并获得锁。
释放锁:
当线程 B 完成临界区的执行或调用 notify/notifyAll 方法时,它会释放锁。释放锁后,cxq 中的一个线程(例如线程 C)会被移到 EntryList 中,准备重新竞争锁。
唤醒等待线程:
notify 方法:唤醒 waitSet 中的一个线程,使其重新竞争锁。
notifyAll 方法:唤醒 waitSet 中的所有线程,它们都会尝试获取锁。
释放锁后的具体行为
释放锁后:
优先从 cxq 中取出一个线程,将其移到 EntryList 中。
EntryList 中的线程会尝试获取锁。
如果 EntryList 为空,才会考虑 waitSet 中的线程。

public class SynchronizedExample {
    private final Object lock = new Object();

    public void methodA() {
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + " entered methodA");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " exiting methodA");
        }
    }

    public void methodB() {
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + " entered methodB");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " exiting methodB");
        }
    }

    public void methodC() {
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + " entered methodC");
            try {
                Thread.sleep(1000);
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " exiting methodC");
        }
    }

    public void methodD() {
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + " entered methodD");
            try {
                Thread.sleep(1000);
                lock.notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " exiting methodD");
        }
    }

    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();

        Thread thread1 = new Thread(() -> example.methodA(), "Thread1");
        Thread thread2 = new Thread(() -> example.methodB(), "Thread2");
        Thread thread3 = new Thread(() -> example.methodC(), "Thread3");
        Thread thread4 = new Thread(() -> example.methodC(), "Thread4");
        Thread thread5 = new Thread(() -> example.methodD(), "Thread5");

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
    }
}

解释

获取锁:
Thread1 和 Thread2 同时尝试进入 methodA 和 methodB,这两个方法都使用同一个 lock 对象。
只有一个线程可以获得锁并进入临界区,另一个线程会被放入 cxq 中等待。
进入临界区:
获得锁的线程进入临界区,执行 System.out.println 和 Thread.sleep 语句。
调用 wait 方法:
Thread3 和 Thread4 在 methodC 中调用 wait 方法,释放锁并被放入 waitSet 中等待。其他线程(例如 Thread5)可以竞争并获得锁。
释放锁:
当 Thread5 在 methodD 中调用 notifyAll 方法时,它会释放锁。
释放锁后,cxq 中的一个线程(例如 Thread1 或 Thread2)会被移到 EntryList 中,准备重新竞争锁。
waitSet 中的所有线程(例如 Thread3 和 Thread4)也会被唤醒并重新竞争锁。
输出结果:
由于 synchronized 的互斥性,输出结果会显示多个线程依次进入和退出临界区。
总结
cxq:竞争队列,用于存储那些未能立即获得锁的线程。
EntryList:等待队列,用于存储那些已经准备好竞争锁的线程。
waitSet:条件等待队列,用于存储那些调用 wait 方法后被阻塞的线程。
通过这三个队列,synchronized 能够在高并发环境下有效地管理线程的竞争和等待,确保线程同步的公平性和效率

### 回答1: "synchronized" 关键字底层原理是基于 Java 的内置锁(Monitor)机制。 Java 中的锁是通过对象的 monitor 实现的,每个对象都有一个对应的 monitor。当线程进入一个对象的 synchronized 代码块时,它会请求该对象的 monitor,如果该 monitor 没有被其他线程占用,则该线程获得该 monitor 并执行代码块,否则该线程阻塞,直到该 monitor 被释放。 通过使用 monitor,Java 可以保证同一时刻只有一个线程能够访问该对象的 synchronized 代码块,从而实现线程同步。 因此,"synchronized" 关键字实现了线程同步的功能,并且可以避免因竞争访问共享资源导致的线程安全问题。 ### 回答2: synchronizedJava中的关键字,用于实现多线程同步。它的底层原理是通过对象的内部锁(也称为监视器锁)来实现线程的互斥访问。 在Java中,每个对象都有一个与之关联的内部锁。当一个线程尝试进入被synchronized修饰的代码块时,它会首先尝试获得该对象的内部锁。如果锁没有被其他线程所占用,那么该线程就会获取到锁,并且进入临界区执行代码。如果锁已经被其他线程所占用,那么该线程就会进入阻塞状态,直到锁被释放。 在synchronized实现中,锁的状态有两种:被线程占用和未被占用。当一个线程获得锁后,它会将锁的状态设置为已被占用。其他线程在尝试获取该锁时,会发现锁已被占用,它们会进入锁的等待队列中,等待获取锁的线程释放锁。 在Java语言规范中,对synchronized关键字进行了优化,包括偏向锁、轻量级锁和重量级锁三种状态,这样可以在不同场景下提高并发性能。 总结来说,synchronized底层原理是通过对象的内部锁来实现线程的互斥访问。通过获取和释放锁的机制,保证了同一时间只有一个线程能够访问被synchronized修饰的代码块,从而保证了线程安全。这种机制虽然简单,但在多线程编程中起着重要的作用。 ### 回答3: synchronizedJava 中用来实现线程同步的关键字。它的底层原理主要是通过对象的监视器锁来实现的。具体来说,Java 中的每个对象都有一个与之相关联的监视器锁,也称为内部锁或互斥锁。 当线程进入一个 synchronized 代码块或方法时,它会尝试获取对象的监视器锁。如果该锁没有被其他线程占用,那么当前线程就可以获取到锁,并进入临界区。如果该锁已经被其他线程占用,则当前线程就会被阻塞,并且进入等待队列。 一旦当前线程进入临界区,它就可以执行 synchronized 代码块或方法中的内容。其他线程如果想要执行该 synchronized 代码块或方法,就必须等待当前线程释放锁。只有当当前线程执行完 synchronized 代码块或方法,且释放了锁,其他线程才有机会获取到锁并执行相应的代码。 synchronized 的原理可以用实例来解释。假设有一个共享资源,例如一个变量,多个线程同时修改该变量的值。如果没有同步机制,可能会导致不可预期的结果。但是当我们使用 synchronized 关键字对修改该变量的代码进行同步,每次只有一个线程能够获取到锁并修改变量的值,这样就保证了线程安全。 总结来说,synchronized 底层原理是通过对象的监视器锁来实现线程之间的同步。它确保了同一时刻只有一个线程能够获取到锁,并且其他线程需要等待锁的释放才能继续执行。这样可以有效地保护共享资源,避免多个线程同时对共享资源进行修改导致的数据不一致性和不可预测性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值