ReentrantLock tryLock(timeout,timeUnit)无法正常工作

时间:2019-04-15 15:51:51

标签: java locking java.util.concurrent


的时候,我对ReentrantLock tryLock(timeout,timeUnit)方法有些困惑 在以下代码下运行,似乎tryLock超时直到上一个线程结束,有人可以解释吗?

public class MyService2 {
public ReentrantLock lock = new ReentrantLock();

public void waitMethod() {
    try {
        System.out.println(System.currentTimeMillis() + " " + Thread.currentThread().getName() + " enter ");
        boolean b = lock.tryLock(2, TimeUnit.SECONDS);
        if (b) {
            System.out.println(System.currentTimeMillis() + " lock begin:" + Thread.currentThread().getName());
            for (int i = 0; i < Integer.MAX_VALUE / 10; i++) {
                Math.random();
            }
            System.out.println(System.currentTimeMillis() + " lock end " + Thread.currentThread().getName());
            return;
        }
        System.out.println(System.currentTimeMillis() + " " + Thread.currentThread().getName() + " got no lock end ");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

public static void main(String[] args) throws InterruptedException {
    MyService2 myService2 = new MyService2();
    Runnable runnable = myService2::waitMethod;
    Thread thread1 = new Thread(runnable);
    thread1.setName("T1");
    thread1.start();
    TimeUnit.MILLISECONDS.sleep(10);
    Thread thread2 = new Thread(runnable);
    thread2.setName("T2");
    thread2.start();
}

运行这段代码后,结果就是这样

1555343172612 T1 enter 
1555343172613 lock begin:T1
1555343172627 T2 enter 
1555343179665 lock end T1
1555343179665 T2 got no lock end 

我的问题是为什么线程T2不会在2秒内超时,而不是等到线程T1结束?

但我刚刚发现:

  1. 例如,如果将Math.random()替换为TimeUnit.SECONDS.sleep(1),它将正常工作。

  2. 如果以调试模式运行,它也可以正常工作。

1 个答案:

答案 0 :(得分:-1)

以下是其中一个替代项,其中进行了数字修改:

首先,清理。更清晰的名称。减少侵入式日志记录。相对时间值。

第二,两个计算线程启动之间的0.1s睡眠被移到每个线程中。显然,这优先于启动计算线程的线程。

第三,启动线程已与计算线程联接。那就是将计算的结论与启动线程联系在一起。在原始代码中,计算线程启动后将无法对其进行管理。如果打算不对计算线程进行托管,则需要对其进行记录。

第四,复制整个启动线程以及两个计算线程结构。这样可以为结构提供一个更实际的运行时环境,并在单个视图中一起呈现结构的不同行为。

修改的主题是提供对程序的预期行为和实际行为(通过日志记录输出查看)的清晰度。目的是为这些提供最大的清晰度。

建议进行其他修改,即将日志语句放入缓存中,然后在所有计算单元完成后显示收集的日志行。这样可以消除由log语句引起的行为更改,这些更改通常是相当大的。

package my.tests;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {

    private static long initialTime;

    protected static void setInitialTime() {
        initialTime = System.currentTimeMillis();
    }

    public static long getInitialTime() {
        return initialTime;
    }

    public static final int CELL_COUNT = 10;

    public static void main(String[] args) {
        setInitialTime();

        System.out.println("Beginning [ " + Integer.toString(CELL_COUNT) + " ] computation cells");

        Thread[] cellThreads = new Thread[CELL_COUNT];
        for ( int cellNo = 0; cellNo < CELL_COUNT; cellNo++ ) {
            final String cellNoText = Integer.toString(cellNo);
            Runnable computeCell = () -> { 
                (new LockTest(cellNoText) ).compute();
            };
            Thread cellThread = new Thread(computeCell);
            cellThreads[cellNo] = cellThread;
        }

        // Start them all up ...

        for ( Thread cellThread : cellThreads ) {
            cellThread.start();
        }

        // Then wait for them all to finish ...

        for ( Thread cellThread : cellThreads ) {
            try {
                cellThread.join();
            } catch ( InterruptedException e ) {
                System.out.println("Unexpected interruption: " + e.getMessage());
                e.printStackTrace();
            }
        }

        System.out.println("Completed [ " + Integer.toString(CELL_COUNT) + " ] computation cells");
    }

    //

    public LockTest(String cellName) {
        this.cellName = cellName;
    }

    private final String cellName;

    public String getCellName() {
        return cellName;
    }

    // Logging ...

    public String formatTime(long timeMs) {
        return String.format("%12d (ms)", new Long(timeMs));
    }

    public long getRelativeTime(long currentTime) {
        return currentTime - getInitialTime();
    }

    public String formatRelativeTime(long timeMs) {
        return String.format(
            "%12d %8d (ms)",
            new Long(timeMs),
            new Long( timeMs - getInitialTime() ));
    }

    public void log(String methodName, String message) {
        long timeMs = System.currentTimeMillis();
        String threadName = Thread.currentThread().getName();

        System.out.println(
            formatRelativeTime(timeMs) + ": " +
            methodName + ": " +
            threadName + ": " + message);
    }

    //

    public void compute() {
        log("compute", "ENTER: " + getCellName());

        Runnable computation = () -> {
            guardedComputation(
                100L, 0,                  // Pause 0.1s before attempting the computation
                1, TimeUnit.SECONDS,      // Try to obtain the computation lock for up to 1.0s.
                Integer.MAX_VALUE / 60 ); // Run this many computations; takes about 2s; adjust as needed
        };

        Thread computer1 = new Thread(computation);
        computer1.setName( getCellName() + "." + "T1");

        Thread computer2 = new Thread(computation);
        computer2.setName( getCellName() + "." + "T2");

        // Run two sets of computations:
        //
        // Each will pause for 0.1s before performing the computations.
        //
        // Performing computations requires a computation lock; wait up to 2.0s
        // to acquire the lock.

        computer1.start();
        computer2.start();

        try {
            computer1.join();
        } catch ( InterruptedException e ) {
            System.out.println("Unexpected interruption: " + e.getMessage());
            e.printStackTrace();
            return;
        }

        try {
            computer2.join();
        } catch ( InterruptedException e ) {
            System.out.println("Unexpected interruption: " + e.getMessage());
            e.printStackTrace();
            return;
        }

        log("compute", "RETURN: " + getCellName());
    }

    // Computation locking ...

    private final ReentrantLock computationLock = new ReentrantLock();

    public boolean acquireComputationLock(long maxWait, TimeUnit maxWaitUnit) throws InterruptedException {
        return computationLock.tryLock(maxWait, maxWaitUnit);
    }

    public void releaseComputationLock() {
        if ( computationLock.isHeldByCurrentThread() ) {
            computationLock.unlock();
        }
    }

    //

    public void guardedComputation(
        long pauseMs, int pauseNs,
        long maxWait, TimeUnit maxWaitUnit, int computations) {

        String methodName = "guardedComputation";

        log(methodName, "ENTER");

        try {
            Thread.sleep(pauseMs, pauseNs);
        } catch ( InterruptedException e ) {
            System.out.println("Unexpected interruption: " + e.getMessage());
            e.printStackTrace();
            return;
        }

        try {
            boolean didLock;
            try {
                didLock = acquireComputationLock(maxWait, maxWaitUnit);
            } catch ( InterruptedException e ) {
                System.out.println("Unexpected interruption: " + e.getMessage());
                e.printStackTrace();
                return;
            }

            String computationsText = Integer.toString(computations);
            if ( didLock ) {
                log(methodName, "Starting computations: " + computationsText);
                for ( int computationNo = 0; computationNo < computations; computationNo++ ) {
                    Math.random();
                }
                log(methodName, "Completed computations: " + computationsText);
            } else {
                log(methodName, "Skipping computations: " + computationsText);
            }

        } finally {
            releaseComputationLock();
        }

        log(methodName, "RETURN");
    }
}