Reetrant блокировки в java

Я новичок в многопоточности в java. Я пытался использовать блокировки. Вот мой пример кода.

package com;

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

public class UsingLocks {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
    // TODO Auto-generated method stub


    UsingLocks job = new UsingLocks();
    Thread [] threads= new Thread[5];
    for(int i=0;i<5;i++){
        threads[i]= new Thread(new LockTask(job));
    }
    for(int i=0;i<5;i++){
        threads[i].start();
    }

}

public void lockingJob() {
    System.out.println("Thread "+Thread.currentThread().getName()+" trying to Acquire lock");
    try {
    lock.tryLock();
    //lock.lock(); //When I use this, code works fine
    int time=new Random().nextInt(10)+3;
    System.out.println("Thread "+Thread.currentThread().getName()+" Acquired lock for "+time+" seconds.");
    TimeUnit.SECONDS.sleep(time);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println("Now releasing lock "+Thread.currentThread().getName());
    lock.unlock();
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println("After Unlock "+Thread.currentThread().getName());



}




}
class LockTask implements Runnable{
UsingLocks job;
public LockTask(UsingLocks job) {
    // TODO Auto-generated constructor stub
    this.job=job;

}

@Override
public void run() {
    // TODO Auto-generated method stub
    job.lockingJob();


}

}

Ниже приведен вывод, когда я использую tryLock()

Thread Thread-1 trying to Acquire lock
Thread Thread-0 trying to Acquire lock
Thread Thread-2 trying to Acquire lock
Thread Thread-1 Acquired lock for 12 seconds.
Thread Thread-2 Acquired lock for 3 seconds.
Thread Thread-0 Acquired lock for 8 seconds.
Thread Thread-3 trying to Acquire lock
Thread Thread-3 Acquired lock for 9 seconds.
Thread Thread-4 trying to Acquire lock
Thread Thread-4 Acquired lock for 6 seconds.
Now releasing lock Thread-2
Exception in thread "Thread-2" java.lang.IllegalMonitorStateException
at      java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)
at com.UsingLocks.lockingJob(UsingLocks.java:37)
at com.LockTask.run(UsingLocks.java:66)
at java.lang.Thread.run(Thread.java:745)
Now releasing lock Thread-4
Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)
at com.UsingLocks.lockingJob(UsingLocks.java:37)
at com.LockTask.run(UsingLocks.java:66)
at java.lang.Thread.run(Thread.java:745)

Теперь, насколько я понимаю, когда первый поток выполняет tryLock(), он должен получить блокировку, а другие потоки не должны иметь возможность получить блокировку. Но, как показано в выводе. После того, как Thread-1 получает блокировку, Thread-2 также приобрел замок и так далее. Как это может быть возможным. Пожалуйста, скажите мне, что мне здесь не хватает. Заранее спасибо.


person Gaurang Agarwal    schedule 20.09.2015    source источник


Ответы (5)


Я немного изменил ваш код и попробовал его для себя.

Я обнаружил, что ReentrantLock.tryLock выдает исключение IllegalMonitorStateException независимо от того, что я делаю. Я не думаю, что это уместно.

package concurrency;

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

/**
 * LockableTask is a nice demonstration
 * @author Michael
 * @link https://stackoverflow.com/questions/32680954/reetrant-locks-in-java
 * @since 9/20/2015 11:25 AM
 */
public class LockableTask implements Runnable {

    private static final int DEFAULT_WAIT = 100;
    private static final int DEFAULT_TIMEOUT = 1000;
    private static final int DEFAULT_THREADS = 5;

    private ReentrantLock lock;
    private int waitPeriod;
    private int timeoutPeriod;
    private boolean halfHeartedLockRequest;

    public static void main(String[] args) {
        long begTime = System.currentTimeMillis();
        System.out.println("Start reentrant lock test");
        try {
            LockableTask lockableTask = new LockableTask(true, false);
            int numThreads = (args.length > 0) ? Integer.parseInt(args[0]) : DEFAULT_THREADS;
            List<Thread> threads = new ArrayList<>(numThreads);
            for (int i = 0; i < numThreads; ++i) {
                threads.add(new Thread(lockableTask));
            }
            for (Thread thread : threads) {
                thread.start();
            }
        } finally {
            long endTime = System.currentTimeMillis();
            System.out.println(String.format("Complete reentrant lock test in %d milliseconds", (endTime-begTime)));
        }
    }

    public LockableTask() {
        this(false, false, DEFAULT_WAIT, DEFAULT_TIMEOUT);
    }

    public LockableTask(boolean halfHeartedLockRequest, boolean fair) {
        this(halfHeartedLockRequest, fair, DEFAULT_WAIT, DEFAULT_TIMEOUT);
    }

    public LockableTask(boolean halfHeartedLockRequest, boolean fair, int waitPeriod, int timeoutPeriod) {
        this.halfHeartedLockRequest = halfHeartedLockRequest;
        this.lock = new ReentrantLock(fair);
        this.waitPeriod = (waitPeriod > 0) ? waitPeriod : DEFAULT_WAIT;
        this.timeoutPeriod = (timeoutPeriod > 0) ? timeoutPeriod : DEFAULT_TIMEOUT;
    }

    @Override
    public void run() {
        System.out.println(String.format("Thread '%s' requests lock ", Thread.currentThread().getName()));
        if (this.halfHeartedLockRequest) {
            this.lock.tryLock();
        } else {
            this.lock.lock();
        }
        try {
            System.out.println(String.format("Thread '%s' acquires lock for %d ", Thread.currentThread().getName(), this.waitPeriod));
            TimeUnit.MILLISECONDS.sleep(this.waitPeriod);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            this.lock.unlock();
            System.out.println(String.format("Thread '%s' releases lock ", Thread.currentThread().getName()));
        }
    }
}
person duffymo    schedule 20.09.2015

Причина в том, что tryLock никогда не блокируется, если блокировка уже удерживается другим потоком.

Ниже приведена документация о tryLock().

публичное логическое значение tryLock()

Получает блокировку только в том случае, если она не удерживается другим потоком во время вызова.

Получает блокировку, если она не удерживается другим потоком, и немедленно возвращается со значением true, устанавливая счетчик удержания блокировки равным единице. Даже если эта блокировка настроена на использование политики честного порядка, вызов tryLock() немедленно получит блокировку, если она доступна, независимо от того, ожидают ли другие потоки блокировки в данный момент. Такое «навязчивое» поведение может быть полезным в определенных обстоятельствах, даже если оно нарушает справедливость. Если вы хотите соблюдать настройку справедливости для этой блокировки, используйте tryLock(0, TimeUnit.SECONDS), который почти эквивалентен (он также обнаруживает прерывание).

Если текущий поток уже удерживает эту блокировку, то счетчик удержания увеличивается на единицу, и метод возвращает значение true.

Если блокировка удерживается другим потоком, этот метод немедленно возвращает значение false.

person Sathish    schedule 20.09.2015

ReentrantLock#tryLock() — получает блокировку только в том случае, если она не удерживается другим потоком во время вызова, и возвращает true, если успешно, иначе false.

Если мы видим трассировку стека -

...
Now releasing lock Thread-2
Exception in thread "Thread-2" java.lang.IllegalMonitorStateException
...
Now releasing lock Thread-4
Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
...

Более очевидно, что потоки, которые не могут получить блокировку, вызывают исключение при попытке ее снять.

person Subhrajyoti Majumder    schedule 20.09.2015
comment
Согласен, я просто хочу знать, что если tryLock() не сможет получить блокировку, то она перейдет к следующей строке? - person Gaurang Agarwal; 20.09.2015

Попробуйте сделать что-нибудь подобное, чтобы было понятнее.

    System.out.println("Thread "+ Thread.currentThread().getName() + " trying to Acquire lock");
    if (lock.tryLock()) {
        try {
            System.out.println("Lock acquired .. ");
            int time=new Random().nextInt(10)+3;
            System.out.println("Thread " + Thread.currentThread().getName() + " Acquired lock for " + time + " seconds.");
            TimeUnit.SECONDS.sleep(time);
       } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
      }
        finally {
            System.out.println("Now releasing lock "+Thread.currentThread().getName());
            lock.unlock();
        }
    } else {
        System.out.println("Thread "+ Thread.currentThread().getName()+ " Failed to acquire lock .. ");
    }
person user2953113    schedule 20.09.2015

Вызов tryLock() просто возвращает логическое значение, оно "не имеет отношения" к тому, будет ли выполняться следующая строка/инструкция, если таковая имеется, или нет. Это причина, почему tryLock() следует использовать в качестве проверки для выполнения некоторых действий/блоков кода, которые нужно синхронизировать. Надеюсь это ответит на твой вопрос.

person javaguy    schedule 21.01.2020