所有线程都被锁定在wait()状态

时间:2015-11-13 19:29:40

标签: java multithreading asynchronous

基本上我必须创建3个类(2个线程)。

首先一个持有一些货物(最小容量(0)和最大容量(200))
第二每隔500ms供货一次 第三每隔500毫秒从货物中取出。

主程序有一个货物类别(1),2个供应商类别(2)和2个减法类别(3)。我遇到的问题是一个接一个,他们陷入wait();状态,永远不会离开。最终所有这些程序都在wait()状态下运行,程序正在运行,但没有它们实际执行任何操作。

头等舱:

public class Storage {
    private int maxCapacity;
    private int currentCapacity;

    public Storage( int currentCapacity, int maxCapacity ) {
        this.currentCapacity = currentCapacity;
        this.maxCapacity = maxCapacity;
    }

    public int getCapacity(){ return this.currentCapacity; }

    public void increase( int q ) {
        this.currentCapacity += q;
        System.out.println("increase" + q + ". Total: " + currentCapacity);
    }

    public int getMax() { return this.maxCapacity; }

    public void decrease( int q ) {
        this.currentCapacity -= q;
        System.out.println("decrease - " + q + ". Total: " + currentCapacity);
    }
}

二等(供应商):

public class Supplier implements Runnable {
    private int capacity;
    private Storage storage;

    private volatile boolean run;

    public Supplier( int capacity, Storage storage ) {
        this.capacity = capacity;
        this.storage = storage;
        this.run = true;
    }

    public void kiss_kill() { run = !run; }

    public synchronized void add() {
        while(storage.getCapacity() + capacity > storage.getMax()) {
            try {
                System.out.println("wait - supplier");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        storage.increase(capacity);
        notifyAll();
    }

    public void run() {
        synchronized (this) {
            while(run) {
                add();
                Thread.yield(); //would be wait(500), but this just speeds it up
            }
        }
    }
}

第3课(接受者/要求者):

public class Taker implements  Runnable {
    private int capacity;
    private Storage storage;

    private volatile boolean run;

    public Taker( int capacity, Storage storage ) {
        this.capacity = capacity;
        this.storage = storage;
        this.run = true;
    }

    public void kiss_kill() { run = !run; }

    public synchronized void take() {
        while(storage.getCapacity() - capacity < 0) {
            try {
                System.out.println("wait - taker");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        storage.decrease(capacity);
        notifyAll();
    }

    public void run() {
        synchronized (this) {
            while(run) {
                take();
                Thread.yield(); //again, wait(500) should be instead
            }
        }
    }
}

主要是这样的:

public class Main{
    public static void main(String... args) {
        Storage sk = new Storage(100, 200);
        Supplier[] s = { new Supplier(10, sk), new Supplier(15, sk) };
        Taker[] p = { new Taker(15, sk), new Taker(20, sk) };
        Thread t[] = {
            new Thread(s[0]),
            new Thread(s[1]),
            new Thread(p[0]),
            new Thread(p[1]) };
        for(Thread th : t) th.start();
        try {
            Thread.sleep(60000); //program should last for 60s.
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        s[0].kiss_kill(); s[1].kiss_kill(); p[0].kiss_kill(); p[1].kiss_kill();
    }
}

为什么不通知notifyAll()释放其他对象的wait()状态?我该怎么做才能解决这个问题?

抱歉,我知道这是一个很长的例子,我讨厌发布太多像这样的课程。谢谢你的阅读!

我翻译了代码,所以如果你发现任何你不确定我错过的东西,请告诉我,我马上就会解决它!

2 个答案:

答案 0 :(得分:2)

做并发很容易:

任何人都可以在方法和synchronized周围的代码块上打synchronized () {}。这并不意味着它是正确的。然后他们可以继续对所有内容发送synchronized,直到它正常工作,直到它没有。

正确执行并发 是硬:

您应该锁定需要保持一致的数据而不是进行更改的方法。而且你必须为所有东西使用相同的锁实例。

在这种情况下,这是currentCapacity中的Storage。这是唯一共享的东西,唯一需要保持一致的东西。

你现在正在做的是让类锁定自己的实例,这意味着没有共享被保护,因为没有共享锁。

考虑一下,如果你没有锁定同一个实例,它必须是一个对象的final那么你在保护什么?

此外,有权访问需要一致且不请求锁定的对象的代码如何。那么它只是做它想要的。调用类中的synchronized() {}不是保护共享数据免受外部操作的方式。

线程安全对象与synchronized关键字无关:

阅读java.util.concurrent包,它包含您已经拥有的所有内容。为您的用例使用正确的数据结构。

在这种特殊情况下,如果您使用AtomicInteger作为计数器,则不需要任何容易出错的手动锁定,任何地方都不需要synchronized,它已经是线程安全的。

不可变数据:

如果你专门使用不可变数据,你就不需要任何这种愚蠢的锁定语义,即使是那些理解它的人也非常容易出错,对那些认为他们理解它的人来说更是如此。

这是一个有用的惯用例:

这是一个了解非确定性方法以及如何在IDE中使用步调试器来调试并发程序的好机会。

Q33700412.java

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import com.vertigrated.FormattedRuntimeException;

public class Q33700412
{
    public static void main(final String[] args)
    {
        final Storage s = new Storage(100);
        final int ap = Runtime.getRuntime().availableProcessors();
        final ExecutorService es = Executors.newFixedThreadPool(ap);
        for (int i = 0; i < ap; i++)
        {
            es.execute(new Runnable()
            {
                final Random r = new Random();

                @Override
                public void run()
                {
                    while (true)
                    {
                         /* this if/else block is NOT thread safe, I did this on purpose
                            the state can change between s.remainingCapacity() and
                            the call to s.increase/s.decrease.
                            This is ok, because the Storage object is internally consistent.
                            This thread might fail if this happens, this is the educational part.
                         */
                        if (s.remainingCapacity() > 0)
                        {
                            if (r.nextBoolean()) { s.increase(r.nextInt(10)); }
                            else { s.decrease(10); }
                            System.out.format("Current Capacity is %d", s.getCurrentCapacity());
                            System.out.println();
                        }
                        else
                        {
                            System.out.format("Max Capacity %d Reached", s.getMaxCapacity());
                            System.out.println();
                        }
                        try { Thread.sleep(r.nextInt(5000)); }
                        catch (InterruptedException e) { throw new RuntimeException(e); }
                    }
                }
            });
        }
        es.shutdown();
        try
        {
            Thread.sleep(TimeUnit.MINUTES.toMillis(1));
            es.shutdown();
        }
        catch (InterruptedException e) { System.out.println("Done!"); }
    }

    public static final class Storage
    {
        /* AtomicInteger is used so that it can be mutable and final at the same time */
        private final AtomicInteger currentCapacity;
        private final int maxCapacity;

        public Storage(final int maxCapacity) { this(0, maxCapacity); }

        public Storage(final int currentCapacity, final int maxCapacity)
        {
            this.currentCapacity = new AtomicInteger(currentCapacity);
            this.maxCapacity = maxCapacity;
        }

        public int remainingCapacity() { return this.maxCapacity - this.currentCapacity.get(); }

        public int getCurrentCapacity() { return this.currentCapacity.get(); }

        public void increase(final int q)
        {
            synchronized (this.currentCapacity)
            {
                if (this.currentCapacity.get() < this.maxCapacity)
                {
                    this.currentCapacity.addAndGet(q);
                }
                else
                {
                    throw new FormattedRuntimeException("Max Capacity %d Exceeded!", this.maxCapacity);
                }
            }
        }

        public int getMaxCapacity() { return this.maxCapacity; }

        public void decrease(final int q)
        {
            synchronized (this.currentCapacity)
            {
                if (this.currentCapacity.get() - q >= 0)
                {
                    this.currentCapacity.addAndGet(q * -1);
                }
                else
                {
                    this.currentCapacity.set(0);
                }
            }
        }
    }
}

注意:

synchronized块的范围限制为保护和锁定需要保持一致的对象所需的最小值。

锁定对象 必须 标记为final,否则引用可能会更改,您将锁定不同的实例。

final越多,您的节目可能是第一次更正确。

答案 1 :(得分:1)

Jarrod Roberson给了你&#34;怎么&#34;一半的答案。这是另一半 - &#34;为什么&#34;。

您的Supplier对象的add()方法会自行等待(即在供应商对象上),并通知自己。

您的Taker对象的take()方法等待自我(即,在接受者对象上),并通知自我。

供应商从不通知接受者,接受者从不通知供应商。

您应该在共享对象上(即在Storage对象上执行所有同步。

  

所以我应该将存储转换为线程吗?

不,您不希望Storage成为线程,您希望它是 lock 。您不应让Supplier个对象和Taker个对象自行同步,而是应该在共享的Storage对象上进行同步。

,例如,这样做:

public void take() {
    synchronized(storage) {
        while(...) {
            try {
                storage.wait();
            } catch ...
        }
        ...
        storage.notifyAll();
    }
}

而不是:

public synchronized void take() {
    while(...) {
        try {
            wait();
        } catch ...
    }
    ...
    notifyAll();
}

对所有其他synchronized方法执行相同操作。