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)