基本上我必须创建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()状态?我该怎么做才能解决这个问题?
抱歉,我知道这是一个很长的例子,我讨厌发布太多像这样的课程。谢谢你的阅读!
我翻译了代码,所以如果你发现任何你不确定我错过的东西,请告诉我,我马上就会解决它!
答案 0 :(得分:2)
任何人都可以在方法和synchronized
周围的代码块上打synchronized () {}
。这并不意味着它是正确的。然后他们可以继续对所有内容发送synchronized
,直到它正常工作,直到它没有。
您应该锁定需要保持一致的数据而不是进行更改的方法。而且你必须为所有东西使用相同的锁实例。
在这种情况下,这是currentCapacity
中的Storage
。这是唯一共享的东西,唯一需要保持一致的东西。
你现在正在做的是让类锁定自己的实例,这意味着没有共享被保护,因为没有共享锁。
考虑一下,如果你没有锁定同一个实例,它必须是一个对象的final
那么你在保护什么?
此外,有权访问需要一致且不请求锁定的对象的代码如何。那么它只是做它想要的。调用类中的synchronized() {}
不是保护共享数据免受外部操作的方式。
阅读java.util.concurrent
包,它包含您已经拥有的所有内容。为您的用例使用正确的数据结构。
在这种特殊情况下,如果您使用AtomicInteger
作为计数器,则不需要任何容易出错的手动锁定,任何地方都不需要synchronized
,它已经是线程安全的。
如果你专门使用不可变数据,你就不需要任何这种愚蠢的锁定语义,即使是那些理解它的人也非常容易出错,对那些认为他们理解它的人来说更是如此。
这是一个了解非确定性方法以及如何在IDE中使用步调试器来调试并发程序的好机会。
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
方法执行相同操作。