Java 多线程我是否正确使用了wait和notify
我写了一个哲学家问题的解决方案。它正在运行,我在控制台上得到了正确的输出,但是Java 多线程我是否正确使用了wait和notify,java,multithreading,Java,Multithreading,我写了一个哲学家问题的解决方案。它正在运行,我在控制台上得到了正确的输出,但是println()之后的wait()从未打印。请告诉我为什么。我在代码中指出了这一点 这个解决方案意味着类似于 在您的代码中,在getArrayOffworks()方法中Fork->used属性总是初始化为false,因此在每个同步块上,if条件总是在等待()处于else条件的位置执行。在您的代码中,在getArrayOffworks()方法中Fork->used属性总是初始化为false,因此,如果wait()在每个
println()
之后的wait()
从未打印。请告诉我为什么。我在代码中指出了这一点
这个解决方案意味着类似于
在您的代码中,在getArrayOffworks()方法中Fork->used属性总是初始化为false,因此在每个同步块上,if条件总是在等待()处于else条件的位置执行。在您的代码中,在getArrayOffworks()方法中Fork->used属性总是初始化为false,因此,如果wait()在每个同步块的else条件中,if条件将始终执行
synchronized (minF) {
if (!minF.used) { // always
minF.used = true;
}
...
minF.used = false;
minF.notify();
}
你正在同步叉子,所以哲学家在检查叉子是否在使用时已经锁定了叉子。
哲学家将fork.used
设置为true,但在离开同步块并释放锁之前将其设置回false
编辑:根据要求,更新代码版本。如果使用已同步的块,则无需自己进行管理:
synchronized (minF) {
synchronized (maxF) {
System.out.println("I am eating right now" + this);
eating();
System.out.println("P" + nr
+ " I have eaten I am giving back the forks");
}
}
如果您想显式地写出它,我将使用java.util.concurrent
类并让Fork从扩展。然后,您的代码如下所示:
叉子:
public class Fork extends Semaphore {
int nr;
public Fork(int nr) {
super(1); // can be handed out to only one person at a time
this.nr = nr;
}
...
哲学家:
minF.acquire();
maxF.acquire();
System.out.println("I am eating right now" + this);
eating();
System.out.println("P" + nr
+ " I have eaten I am giving back the forks");
maxF.release();
minF.release();
你正在同步叉子,所以哲学家在检查叉子是否在使用时已经锁定了叉子。
哲学家将fork.used
设置为true,但在离开同步块并释放锁之前将其设置回false
编辑:根据要求,更新代码版本。如果使用已同步的块,则无需自己进行管理:
synchronized (minF) {
synchronized (maxF) {
System.out.println("I am eating right now" + this);
eating();
System.out.println("P" + nr
+ " I have eaten I am giving back the forks");
}
}
如果您想显式地写出它,我将使用java.util.concurrent
类并让Fork从扩展。然后,您的代码如下所示:
叉子:
public class Fork extends Semaphore {
int nr;
public Fork(int nr) {
super(1); // can be handed out to only one person at a time
this.nr = nr;
}
...
哲学家:
minF.acquire();
maxF.acquire();
System.out.println("I am eating right now" + this);
eating();
System.out.println("P" + nr
+ " I have eaten I am giving back the forks");
maxF.release();
minF.release();
flup已经回答了您的问题,但是移动同步块是不够的;如果您想将您的
used
标志与wait
和notify
一起使用,您需要检查循环中等待的条件,因为即使没有notify
,等待也可能返回
一种解决方案可能是:
public class Demo
{
public static class Philosopher
extends Thread
{
String name;
int nr;
Fork left, right;
public Philosopher( String name, int nr, Fork left, Fork right )
{
this.nr = nr;
this.name = name;
this.left = left;
this.right = right;
System.out.println( "NR " + nr + " " + left + " " + right );
}
@Override
public void run()
{
// while ( true )
try {
Fork minF = Fork.min( left, right );
Fork maxF = Fork.max( left, right );
synchronized ( minF ) {
if ( ! minF.used ) {
minF.used = true;
System.out.println( "P" + nr + " took fork " + minF );
} else {
while ( minF.used )
// <- YOU NEED TO CHECK THIS IN A LOOP
minF.wait();
minF.used = true;
System.out.println( "I waited and took fork " + minF ); // why it is never PRINTEDDD???
}
}
synchronized ( maxF ) {
if ( ! maxF.used ) {
maxF.used = true;
System.out.println( "P" + nr + " took fork " + maxF );
} else {
while ( maxF.used )
// <- YOU NEED TO CHECK THIS IN A LOOP
maxF.wait();
maxF.used = true;
System.out.println( "I waited and took fork " + maxF ); // why it is never PRINTEDDD??
}
}
System.out.println( "I am eating right now" + this );
eating();
System.out.println( "P" + nr + " I have eaten I am giving back the forks" );
synchronized ( minF ) {
minF.used = false;
System.out.println( "P" + nr + " NOTIFY fork" + minF );
minF.notify();
}
synchronized ( maxF ) {
maxF.used = false;
System.out.println( "P" + nr + " NOTIFY fork" + maxF );
maxF.notify();
}
} catch ( Exception e ) {
// ignore
}
}
public void eating()
throws InterruptedException
{
int time = (int) ( Math.random() * 2000 );
for ( int i = 0; i < 5; i ++ ) {
System.out.println( "P" + nr + " " + i );
Thread.sleep( time / 5 );
}
}
public String toString()
{
return "Philosopher " + nr;
}
public static void startPhilosophers( Philosopher[] f )
{
for ( int i = f.length - 1; i >= 0; i -- ) {
f[ i ].start();
}
}
}
public static class Fork
{
boolean used;
int nr;
public Fork( boolean used, int nr )
{
this.used = used;
this.nr = nr;
}
@Override
public String toString()
{
return "F" + nr;
}
public static Fork min( Fork l, Fork p )
{
if ( l.nr < p.nr )
return l;
return p;
}
public static Fork max( Fork l, Fork p )
{
if ( l.nr > p.nr )
return l;
return p;
}
public static Fork[] getArrayOfForks()
{
Fork[] t = new Fork[ 5 ];
for ( int i = 0; i < t.length; i ++ ) {
t[ i ] = new Fork( false, ( i + 1 ) );
}
return t;
}
}
public static void main( String[] args )
{
Fork[] t = Fork.getArrayOfForks();
Philosopher[] f =
{ new Philosopher( "philosopher 1", 1, t[ 0 ], t[ 4 ] ), new Philosopher( "philosopher 2", 2, t[ 1 ], t[ 0 ] ),
new Philosopher( "philosopher 3", 3, t[ 2 ], t[ 1 ] ), new Philosopher( "philosopher 4", 4, t[ 3 ], t[ 2 ] ),
new Philosopher( "philosopher 5", 5, t[ 4 ], t[ 3 ] ), };
Philosopher.startPhilosophers( f );
}
}
flup已经回答了您的问题,但是移动同步块是不够的;如果您想将您的
used
标志与wait
和notify
一起使用,您需要检查循环中等待的条件,因为即使没有notify
,等待也可能返回
一种解决方案可能是:
public class Demo
{
public static class Philosopher
extends Thread
{
String name;
int nr;
Fork left, right;
public Philosopher( String name, int nr, Fork left, Fork right )
{
this.nr = nr;
this.name = name;
this.left = left;
this.right = right;
System.out.println( "NR " + nr + " " + left + " " + right );
}
@Override
public void run()
{
// while ( true )
try {
Fork minF = Fork.min( left, right );
Fork maxF = Fork.max( left, right );
synchronized ( minF ) {
if ( ! minF.used ) {
minF.used = true;
System.out.println( "P" + nr + " took fork " + minF );
} else {
while ( minF.used )
// <- YOU NEED TO CHECK THIS IN A LOOP
minF.wait();
minF.used = true;
System.out.println( "I waited and took fork " + minF ); // why it is never PRINTEDDD???
}
}
synchronized ( maxF ) {
if ( ! maxF.used ) {
maxF.used = true;
System.out.println( "P" + nr + " took fork " + maxF );
} else {
while ( maxF.used )
// <- YOU NEED TO CHECK THIS IN A LOOP
maxF.wait();
maxF.used = true;
System.out.println( "I waited and took fork " + maxF ); // why it is never PRINTEDDD??
}
}
System.out.println( "I am eating right now" + this );
eating();
System.out.println( "P" + nr + " I have eaten I am giving back the forks" );
synchronized ( minF ) {
minF.used = false;
System.out.println( "P" + nr + " NOTIFY fork" + minF );
minF.notify();
}
synchronized ( maxF ) {
maxF.used = false;
System.out.println( "P" + nr + " NOTIFY fork" + maxF );
maxF.notify();
}
} catch ( Exception e ) {
// ignore
}
}
public void eating()
throws InterruptedException
{
int time = (int) ( Math.random() * 2000 );
for ( int i = 0; i < 5; i ++ ) {
System.out.println( "P" + nr + " " + i );
Thread.sleep( time / 5 );
}
}
public String toString()
{
return "Philosopher " + nr;
}
public static void startPhilosophers( Philosopher[] f )
{
for ( int i = f.length - 1; i >= 0; i -- ) {
f[ i ].start();
}
}
}
public static class Fork
{
boolean used;
int nr;
public Fork( boolean used, int nr )
{
this.used = used;
this.nr = nr;
}
@Override
public String toString()
{
return "F" + nr;
}
public static Fork min( Fork l, Fork p )
{
if ( l.nr < p.nr )
return l;
return p;
}
public static Fork max( Fork l, Fork p )
{
if ( l.nr > p.nr )
return l;
return p;
}
public static Fork[] getArrayOfForks()
{
Fork[] t = new Fork[ 5 ];
for ( int i = 0; i < t.length; i ++ ) {
t[ i ] = new Fork( false, ( i + 1 ) );
}
return t;
}
}
public static void main( String[] args )
{
Fork[] t = Fork.getArrayOfForks();
Philosopher[] f =
{ new Philosopher( "philosopher 1", 1, t[ 0 ], t[ 4 ] ), new Philosopher( "philosopher 2", 2, t[ 1 ], t[ 0 ] ),
new Philosopher( "philosopher 3", 3, t[ 2 ], t[ 1 ] ), new Philosopher( "philosopher 4", 4, t[ 3 ], t[ 2 ] ),
new Philosopher( "philosopher 5", 5, t[ 4 ], t[ 3 ] ), };
Philosopher.startPhilosophers( f );
}
}
例如,当P5将首先使用他的右fork时,P4将看到此fork(hist-left)已被使用(used==true),因此应执行else。当示例P5将首先使用他的右fork时,P4将看到此fork(hist-left)已被使用(used==true),因此应执行else。请编辑我的代码以显示其工作原理?我想他在通知其他哲学家叉子可以自由使用之前必须将其设置为false。你能编辑我的代码来说明它是如何工作的吗?我认为,在他通知其他哲学家叉子可以自由使用之前,他必须将其设置为false。如果他以另一种方式执行,它将通知fork可以自由使用,但是fork.used字段将为TRUE而不是false。同步块为您完成工作。我已经添加了一个更简单的代码版本,我认为这也能起到作用。并且添加了一个使用显式信号量的版本。你能编辑我的代码来展示它是如何工作的吗?我想他在通知其他哲学家叉子可以自由使用之前必须将其设置为false。你能编辑我的代码来说明它是如何工作的吗?我认为,在他通知其他哲学家叉子可以自由使用之前,他必须将其设置为false。如果他以另一种方式执行,它将通知fork可以自由使用,但是fork.used字段将为TRUE而不是false。同步块为您完成工作。我已经添加了一个更简单的代码版本,我认为它也可以做到这一点。并且添加了一个使用显式信号量的版本。是的,您可以使用wait/notify,但是对于这个用例来说,它太重了。第二个示例正是我写的,不是吗?第一个解决方案是显示需要修复的内容。我假设OP想要实现这个来学习线程。是的,第二个例子是你答案的完整版本,但是你的答案在我写的时候没有包含那个代码。(没有同步的并行工作的刻痕…:D)是的,您可以使用wait/notify,但是对于这个用例来说它太重了。第二个示例正是我写的,不是吗?第一个解决方案是显示需要修复的内容。我假设OP想要实现这个来学习线程。是的,第二个例子是你答案的完整版本,但是你的答案在我写的时候没有包含那个代码。(没有同步的平行作品的雕刻…:D)