Java线程:wait,notifyAll和synchronized关键字未按预期工作

时间:2013-10-09 10:16:06

标签: java multithreading concurrency

当前状态:(2013年10月10日):已解决。

问题: 我正在研究线程并决定随机实现一些任务。 所以谷歌发现了这个assignment

描述(消除游戏逻辑,主要目的是专注于线程) 我有四个玩家围成一圈,卡片在它们之间划分。他们将洗牌他们的牌(这个任务是为了简单,显然游戏逻辑是不同的),一旦完成他们的卡他们可以举手他们完成,然后所有人都可以重复这个过程,在技术术语线程将等待其他人完成,然后到达或完成最后的人可以通知其他人继续......

我的代码状态:

  1. 不同的线程同时进入同步块。
  2. 一旦一个线程完成了他的工作,它应该递增count变量并且count = 4当前线程应该通知其他3个等待,最终我想实现之前发生的关系。
  3. 主要类别:

       public class RunGame implements Runnable{
    
        volatile int count=0;
        public int getCount() {
            return count;
        }
        public void setCount(int count) {
            this.count = count;
        }
        public static void main(String arg[]){
    
        RunGame obj= new RunGame();
    
        Player p1= new Player("Player 1");
        Player p2= new Player("Player 2");
        Player p3= new Player("Player 3");
        Player p4= new Player("Player 4");
    
        Runnable r1 = new ThreadRun(p1,obj);
        Runnable r2 = new ThreadRun(p2,obj);
        Runnable r3 = new ThreadRun(p3,obj);
        Runnable r4 = new ThreadRun(p4,obj);
    
        Thread t1 = new Thread(r1,"Player 1");
        Thread t2 = new Thread(r2,"Player 2");
        Thread t3 = new Thread(r3,"Player 3");
        Thread t4 = new Thread(r4,"Player 4");
    
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        }
        }
    

    型号:

      public class Player {
        private String player;
    
        public Player(String name) {
            this.player=name;
        }
    
        //getter and setter
    }
    

    商务舱:

        public class PlayerRun implements Runnable{
    
            Player player;
    
        RunGame mainObj;
        public PlayerRun(Player player,RunGame main) {
            this.player=player;
            this.mainObj=main;
        }
    
        public void run() {
            while(true){
            synchronized (mainObj) {
                int count=mainObj.getCount();
                System.out.println(Thread.currentThread().getName()+"...."+count);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                mainObj.setCount(++count);
                System.out.println(Thread.currentThread().getName()+" Done...."+count);
            }
    
            synchronized (mainObj) {
                try {
                    if(mainObj.getCount()<=3)
                        mainObj.wait();//current thread will wait till it is awaken by notify.
                    else if(mainObj.getCount()>3){
                        System.out.println(Thread.currentThread().getName()+" is last one to enter and awake all");
                        mainObj.setCount(0);
                        mainObj.notifyAll();
                    }
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"==============================");
    
            }
        }}
    

    输出:

        Player 1....0
    Player 1 Done....1
    Player 4....1
    Player 4 Done....2
    Player 3....2
    Player 3 Done....3
    Player 2....3
    Player 2 Done....4
    Player 2 is last one to enter and awake all
    Player 2==============================
    Player 2....0
    Player 3==============================
    Player 4==============================
    Player 2 Done....1
    Player 1==============================
    Player 4....1
    Player 4 Done....2
    Player 3....2
    Player 3 Done....3
    Player 1....3
    Player 1 Done....4
    Player 1 is last one to enter and awake all
    Player 1==============================
    Player 3==============================
    Player 1....0
    Player 1 Done....1
    Player 4==============================
    Player 3....1
    Player 2==============================
    Player 3 Done....2
    Player 2....2
    Player 2 Done....3
    Player 4....3
    Player 4 Done....4
    Player 4 is last one to enter and awake all
    Player 4==============================
    Player 4....0
    Player 3==============================
    Player 2==============================
    Player 4 Done....1
    Player 1==============================
    Player 2....1
    Player 2 Done....2
    Player 3....2
    Player 3 Done....3
    Player 1....3
    Player 1 Done....4
    Player 1 is last one to enter and awake all
    Player 1==============================
    Player 3==============================
    Player 1....0
    Player 1 Done....1
    Player 2==============================
    Player 4==============================
    Player 3....1
    Player 3 Done....2
    Player 4....2
    Player 4 Done....3
    Player 2....3
    Player 2 Done....4
    Player 2 is last one to enter and awake all
    Player 2==============================
    Player 3==============================
    Player 2....0
    Player 1==============================
    Player 4==============================
    Player 2 Done....1
    Player 4....1
    Player 4 Done....2
    Player 1....2
    Player 1 Done....3
    Player 3....3
    Player 3 Done....4
    Player 3 is last one to enter and awake all
    Player 3==============================
    Player 3....0
    Player 1==============================
    Player 3 Done....1
    Player 4==============================
    Player 1....1
    Player 2==============================
    Player 1 Done....2
    Player 2....2
    Player 2 Done....3
    Player 4....3
    Player 4 Done....4
    Player 4 is last one to enter and awake all
    Player 4==============================
    Player 2==============================
    Player 3==============================
    Player 1==============================
    Player 4....0
    Player 4 Done....1
    Player 1....1
    Player 1 Done....2
    Player 3....2
    Player 3 Done....3
    Player 2....3
    Player 2 Done....4
    Player 2 is last one to enter and awake all
    

3 个答案:

答案 0 :(得分:1)

您的同步块会为每个玩家单独锁定,因此锁定必然会失败。 见:

synchronized (this) {

                try {

                System.out.println(Thread.currentThread().getName()+" work in progress..."+count);
                for(int i =0;i<=3;i++){
                    Thread.sleep(1000);
                }
                count++;
                System.out.println(Thread.currentThread().getName()+" work Done..."+count);

} 所有玩家都应该锁定同一个物体。

因此,您可以为所有玩家创建一个公共锁,并将其传递给PlayerRun以解决此问题。

编辑:

根据以下评论,用户要求是

  

“如果我理解正确,他希望球员做他们的工作   (同时)然后每个完成的人都会等到最后一个   完成了他的工作,他们又重新开始了。“

这增加了一个全新的问题。在这种情况下,同步块采用的方法是不正确的,因为它将意味着相互排斥的方法。 您应该使用CyclicBarrier,这是所有线程的单一障碍,您可以实现此目标。

答案 1 :(得分:1)

  
      
  1. 不同的线程同时进入同步块。
  2.   

同步机制用于在多线程环境中协调访问共享变量。每当多个线程访问给定的状态变量,并且其中一个必须写入它时,它们都必须使用同步协调它们对它的访问。

您在synchronized(this)类的run方法中使用PlayerRun,该方法表示只有一个thread of execution可以在任何给定的时间点执行此代码块。由于您创建了4个PlayerRun个实例并让它们彼此独立运行,因此它们会同时输入自己的synchronized个块。

您再次将count变量定义为静态:

static volatile int count=0;

您需要使用PlayerRun的运行时类作为监视器,因为它不依赖于PlayerRun的任何特定实例。因此count变量的更改应在PlayerRun.class上同步:

synchronized(PlayerRun.class)
{  
   count++;  
} 
  

一旦一个线程完成了他的工作,它应该递增count变量并且count = 4当前线程应该通知其他3个等待,最终我想实现之前发生的关系。

方法wait, notify and notifyAll为一组线程提供了一种等待特定条件变为真的方法(这些都是实例方法)。在某个对象上使用wait时,表示当前执行的线程将等待某个条件成为该对象的真实状态。而notifynotifyAll用于通知等待同一对象的其他线程。当多个线程使用同一个对象时,使用这些方法集,并且编写代码的方式与此方法不同。

我的建议是在所有线程中使用一个共享counter。这可以通过在count类中定义RunGame变量并将其实例传递给PlayerRun runnables来实现:

public class RunGame implements Runnable{

private int count=0;
...
public synchronized void increaseCount() throws InterruptedException {
      count++;
}
....
@Override
public void run() {
Runnable r1 = new PlayerRun(player1, this);
...
}
}

您可以更改PlayerRun

public class PlayerRun implements Runnable{
    RunGame runGame;
    ...
    public PlayerRun(Player player, RunGame runGame) {
        this.player = player;
        this.runGame = runGame; 
    }
    ...
    @Override
    public void run() {
    // DO YOUR WORK
    runGame.increaseCount();
    }
}

答案 2 :(得分:1)

  

实际上每个线程都会在轮次上更新计数,所以在计数3之后它将变为3,当第4个线程进入时它将增加到4并通知所有其他人重复该过程。

上述声明是从comment复制的。而且我认为这会改变最初的需求,因为现在你想重新重复这个过程。为此,您需要使用两个方法替换increaseCount方法,一个用于使线程等待count < 4,另一个用于在计数变为4时通知等待线程:

   //threads will increment the value of count by 1 and will wait if its value is
   //less than 4.
   public synchronized void increaseCount() throws InterruptedException {
        count++;
        if (count < 4) {
            wait();
        }
    }

    //4th thread will come inside this method, it will set the count value to 0
    //and will notify other threads
    public synchronized void releaseCount() throws InterruptedException {
        if (count >= 3) {
            count = 0;
            notifyAll();
        }

    }

并将PlayerRun更改为:

public class PlayerRun implements Runnable{
    RunGame runGame;
    ...
    public PlayerRun(Player player, RunGame runGame) {
        this.player = player;
        this.runGame = runGame; 
    }
    ...
    @Override
    public void run() {
    while (true) {
    // DO YOUR WORK
    runGame.releaseCount();
    runGame.increaseCount();
    }
    }
}

您无需在synchronized run方法中使用PlayerRun关键字。 synchronized关键字用于协调对共享变量的访问。您在count中拥有共享变量RunGame,您可以使用synchronizedincreaseCount上的relaesCount关键字向其提供同步访问权。