java 中有关线程通信的锁定和条件

Lock and Condition about thread communication in java

我是 java 初学者,我在 java 中学习 Thread 时编写了以下代码。我想,如果我lock inResource.set(),注释掉Lock.unlock()Resource.out()里面的代码执行不了,因为我想执行out方法的时候无法unlock in。顺便说一句,无论我在set()out()中注释掉解锁,程序都会这样执行:

Thread[Thread-1,5,main]....Produce....chicken1
Thread[Thread-2,5,main]....Consume..........chicken1
Thread[Thread-0,5,main]....Produce....chicken2
Thread[Thread-3,5,main]....Consume..........chicken2 ......

想了半天没搞明白。刚学的,可能是我理解有误,希望各位大侠指教。 请原谅我糟糕的英语。非常感谢你。我的代码在这里:

package Thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadStudying {

public static void main(String[] args) {
    Resource r = new Resource();
    Thread t0 = new Thread(new Producer(r));
    Thread t1 = new Thread(new Producer(r));
    Thread t2 = new Thread(new Consumer(r));
    Thread t3 = new Thread(new Consumer(r));

    t0.start();
    t1.start();
    t2.start();
    t3.start();
}

static class Resource {
    private String name;
    private int count = 1;
    boolean isOut = false;

    Lock lock = new ReentrantLock();
    Condition pro_con = lock.newCondition();
    Condition consu_con = lock.newCondition();

    public void set(String name) {
        lock.lock();
        try {
            while (isOut) {
                try {
                    pro_con.await();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            this.name = name + count;
            System.out.println(Thread.currentThread() + "....Produce...." + this.name);
            count++;

            isOut = true;
            consu_con.signal();
        }
        finally {
            lock.unlock();
        }
    }

    public void out() {
        lock.lock();
        try {
            while (!isOut) {
                try {
                    consu_con.await();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println(Thread.currentThread() + "....Consume.........." + this.name);

            isOut = false;
            pro_con.signal();
        }
        finally {
            //lock.unlock();
        }
    }
}

static class Producer implements Runnable {
    Resource r;

    Producer(Resource r) {
        this.r = r;
    }

    public void run() {
        while (true) {
            r.set("chicken");

            try {
                Thread.sleep(500);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

static class Consumer implements Runnable {
    Resource r;

    Consumer(Resource r) {
        this.r = r;
    }


    @Override
    public void run() {
        while (true) {
            r.out();
            try {
                Thread.sleep(500);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
  }
}

在生产者和消费者中,您通过

重复调用lock.await
while (true) {
    //
}

来自 doc,当您调用 lock.await 时:

The lock associated with this Condition is atomically released

所以不管你注释掉lock.unlock与否,生产者和消费者都不会被阻塞

P.S. 使用以下代码记录有关获取和释放锁的更多详细信息:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadStudying {

public static void main(String[] args) {
    Resource r = new Resource();
    Thread t0 = new Thread(new Producer(r), "Producer 1");
    Thread t1 = new Thread(new Producer(r), "Producer 2");
    Thread t2 = new Thread(new Consumer(r), "Consumer 1");
    Thread t3 = new Thread(new Consumer(r), "Consumer 2");

    t0.start();
    t1.start();
    t2.start();
    t3.start();
}

static class Resource {
    private String name;
    private int count = 1;
    boolean isOut = false;

    Lock lock = new ReentrantLock();
    Condition pro_con = lock.newCondition();
    Condition consu_con = lock.newCondition();

    public void set(String name) {
        System.out.println(Thread.currentThread() + "before lock");
        lock.lock();
        System.out.println(Thread.currentThread() + "get lock");
        try {
            while (isOut) {
                try {
                    System.out.println(Thread.currentThread() + "release lock");
                    pro_con.await();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            this.name = name + count;
            System.out.println(Thread.currentThread() + "....Produce...." + this.name);
            count++;

            isOut = true;
            consu_con.signal();
        }
        finally {

        }
    }

    public void out() {
        System.out.println(Thread.currentThread() + "before lock");
        lock.lock();
        System.out.println(Thread.currentThread() + "get lock");
        try {
            while (!isOut) {
                try {
                    System.out.println(Thread.currentThread() + "release lock");
                    consu_con.await();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println(Thread.currentThread() + "....Consume.........." + this.name);

            isOut = false;
            pro_con.signal();
        }
        finally {
            //lock.unlock();
        }
    }
}

static class Producer implements Runnable {
    Resource r;

    Producer(Resource r) {
        this.r = r;
    }

    public void run() {
        while (true) {
            r.set("chicken");

            try {
                Thread.sleep(500);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

static class Consumer implements Runnable {
    Resource r;

    Consumer(Resource r) {
        this.r = r;
    }


    @Override
    public void run() {
        while (true) {
            r.out();
            try {
                Thread.sleep(500);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
  }
}

FirstOfAll,"if I lock in Resource.set() and comment out the Lock.unlock(), the code in Resource.out() can't be executed "。你这个说法是错误的。

让我解释一下为什么,

在您发布的代码中,out() 没有解锁。我假设您认为 Consumer 线程之一(t2t3)在获取锁方面没有问题。

所以假设 t2 在进入 out() 方法时获得了锁,而在退出 out() 方法时没有释放锁。但是你忽略了 out() 方法在 Consumer Runnable 的 run() 方法内部无限循环执行的事实。所以当t2退出out()时,休眠500毫秒;它仍然拥有锁。当它在下一次迭代中进入 out() 方法时,它会在它已经拥有的同一个锁上执行 Lock.lock()。由于锁是ReentrantLock,所以它会继续执行await()释放锁的地方;等待锁的其他线程(Producer 个线程)有机会获得锁。