混淆了java线程池和ReentrantLock

混淆了java线程池和ReentrantLock,java,multithreading,reentrantlock,Java,Multithreading,Reentrantlock,我得到固定数量的两个线程,然后提交100个任务,其中我 使用了一个锁并故意将其解锁,此代码的运行结果为 从1到99排序的数字,这让我感到困惑: 1) 是因为线程被重用,所以同一个线程可以多次获取它吗 2) 如果是这样,锁不阻塞线程,它还能被重用吗?锁防护装置的功能仅限于其范围内的线路 请纠正我 public class LockTest { public static volatile int a = 1; static final ReentrantLock lock =

我得到固定数量的两个线程,然后提交100个任务,其中我

使用了一个锁并故意将其解锁,此代码的运行结果为

从1到99排序的数字,这让我感到困惑:

1) 是因为线程被重用,所以同一个线程可以多次获取它吗

2) 如果是这样,锁不阻塞线程,它还能被重用吗?锁防护装置的功能仅限于其范围内的线路

请纠正我

public class LockTest {

    public static volatile int a = 1;

    static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        for (int k = 0; k < 100; k++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    System.out.println(a++);
                }
            });
        }
        executorService.shutdown();
    }
}
公共类锁测试{
公共静态易失性int a=1;
静态最终ReentrantLock=新的ReentrantLock();
公共静态void main(字符串[]args){
ExecutorService ExecutorService=Executors.newFixedThreadPool(2);
对于(int k=0;k<100;k++){
executorService.submit(新的Runnable(){
@凌驾
公开募捐{
lock.lock();
System.out.println(a++);
}
});
}
executorService.shutdown();
}
}

我认为你的分析是正确的

  • 是的,获取锁的第一个线程持有它。第二个线程在锁调用时阻塞
  • 是的,
    ReentrantLock
    的要点是同一个线程可以多次锁定它。锁保护的不是线路,而是锁本身
    如果您希望其他线程能够获得锁,那么每个
    lock()
    调用都必须有相应的
    unlock()
    调用。

    我认为您的分析是正确的

  • 是的,获取锁的第一个线程持有它。第二个线程在锁调用时阻塞
  • 是的,
    ReentrantLock
    的要点是同一个线程可以多次锁定它。锁保护的不是线路,而是锁本身
    如果希望其他线程能够获得锁,则每个
    lock()
    调用必须具有相应的
    unlock()
    调用。

    如果以这种方式修改代码,则您可以获得自己回答问题所需的所有信息:

    public static void main(String[] args) {
        ExecutorService executorService = newFixedThreadPool(2);
        for (int k = 0; k < 100; k++) {
            executorService.submit(() -> {
                lock.lock();
                System.out.println(currentThread().getId() +
                                 " hold count: " + lock.getHoldCount());
                System.out.println("a = " + a++);
            });
        }
        executorService.shutdown();
    }
    
    如你所见:

    • 您想输出
      100
      数字,但只输出
      99
    • 输出显示只有一个线程在工作,因为您从未释放第一个线程获得的锁。第二个线程等待获得锁
    • 因为您使用了一个
      ReentrantLock
      ,所以获得锁的第一个线程可能会继续工作,因为他已经拥有了锁
    • JVM永远不会关闭,因为第一个线程永远不会释放锁,因此第二个线程将永远等待
    回答PO在评论中提出的问题

    这是可以预测的:缓存线程池将根据需要动态创建新的
    线程。发生的情况是:

    • 第一个线程将永远获得锁(因为它永远不会释放锁)。它将尽可能快地处理提交的任务
    • 当第一个线程正在处理时,
      CachedThreadPool
      将创建新线程以执行已提交但尚未处理的其余任务
    • 根据获得锁的第一个线程处理尚未安排给其他线程的提交任务的速度,最终将有许多线程永远等待

    如果您以这种方式修改代码,您将获得自己回答问题所需的所有信息:

    public static void main(String[] args) {
        ExecutorService executorService = newFixedThreadPool(2);
        for (int k = 0; k < 100; k++) {
            executorService.submit(() -> {
                lock.lock();
                System.out.println(currentThread().getId() +
                                 " hold count: " + lock.getHoldCount());
                System.out.println("a = " + a++);
            });
        }
        executorService.shutdown();
    }
    
    如你所见:

    • 您想输出
      100
      数字,但只输出
      99
    • 输出显示只有一个线程在工作,因为您从未释放第一个线程获得的锁。第二个线程等待获得锁
    • 因为您使用了一个
      ReentrantLock
      ,所以获得锁的第一个线程可能会继续工作,因为他已经拥有了锁
    • JVM永远不会关闭,因为第一个线程永远不会释放锁,因此第二个线程将永远等待
    回答PO在评论中提出的问题

    这是可以预测的:缓存线程池将根据需要动态创建新的
    线程。发生的情况是:

    • 第一个线程将永远获得锁(因为它永远不会释放锁)。它将尽可能快地处理提交的任务
    • 当第一个线程正在处理时,
      CachedThreadPool
      将创建新线程以执行已提交但尚未处理的其余任务
    • 根据获得锁的第一个线程处理尚未安排给其他线程的提交任务的速度,最终将有许多线程永远等待

    为什么没有解锁呼叫?永远不会解锁的锁有什么意义?@Sidias Korrado是的,我让它解锁,我只是想弄清楚它是如何工作的你让它锁定,而不是解锁。看看,希望它能回答你的问题它不会,有一个
    unlock()
    调用。为什么没有解锁调用?永远不会解锁的锁有什么意义?@Sidias Korrado是的,我让它解锁,我只是想弄清楚它是如何工作的你让它锁定,而不是解锁。看看,希望它能回答你的问题它不会,有一个
    unlock()
    call.thx,很清楚,但是如果我将executorService更改为Executors.newCachedThreadPool(),结果是不可预测的。thx,非常清楚,但是如果我将executorService更改为Executors.newCachedThreadPool(),结果是不可预测的。