简体   繁体   English

Java线程:等待,notifyAll和同步的关键字无法按预期工作

[英]Java Thread: wait, notifyAll and synchronized keywords not working as expected

Current Status: (Oct 10,2013): Solved. 当前状态: (2013年10月10日):已解决。

Problem: I was studying the threads and decided to implement some assignment randomly. 问题:我正在研究线程,并决定随机实现一些分配。 So Google one and found this assignment 因此,谷歌找到了这份作业

Description (Eliminating the game logic, main aim is to focus on the threads) I have four players sitting in a circle, Cards have been divided between them. 描述 (消除游戏逻辑,主要目的是专注于线程)我有四个玩家围成一圈,纸牌已经划分在他们之间。 They will shuffle the their deck of cards (This task is for simplicity, obviously game logic is different), once all are done with their cards they can raise hands that they are done and then all can repeat the process, In technical terms threads will wait for others to finish and then whoever reaching or finishing last can notify others to continue... 他们将洗牌(此任务是为了简单起见,显然游戏逻辑是不同的),一旦用卡完成所有操作,他们就可以举手完成操作,然后所有人都可以重复该过程。等待其他人完成,然后最后到达或最后完成的人可以通知其他人继续...

My code status: 我的代码状态:

  1. Different threads are entering in synchronized block at same time. 不同线程同时进入同步块。
  2. Once a thread completes his work, it should increment the count variable and count=4 current thread should notify the 3 other waiting for, ultimately I want to achieve happens-before relationship. 一旦一个线程完成了他的工作,它应该增加count变量,并且count = 4当前线程应该通知其他3个等待,最终我想实现事前发生的关系。

Main Class: 主类:

   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();
    }
    }

Model: 模型:

  public class Player {
    private String player;

    public Player(String name) {
        this.player=name;
    }

    //getter and setter
}

Business Class: 商务课程:

    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()+"==============================");

        }
    }}

Output: 输出:

    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

Your synchronize block takes separate lock for each player so locking is bound to fail. 您的同步块为每个播放器分别锁定,因此锁定肯定会失败。 See this: 看到这个:

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);

} All the players should take lock on same object. }所有玩家都应锁定同一物体。

So you can create a common lock for all players and pass it down to PlayerRun to resolve this issue. 因此,您可以为所有玩家创建一个通用锁,并将其传递给PlayerRun来解决此问题。

EDIT: 编辑:

As per below comments user requirements is 根据以下评论用户需求是

" If I understand it correctly he wants the players to do their work (simultaneously) then everyone who's finished waits until the last one is done with his work and they begin again." “如果我理解正确,他希望球员(同时)完成他们的工作,那么每个完成的人都会等到最后一个工作完成之后,他们才能重新开始。”

This adds a completely new dimension tot he question. 这为他提出了一个全新的维度。 The approach taken of synchronized block is incorrect in this case as it will mean mutually exclusive approach. 在这种情况下,采用同步块的方法是不正确的,因为这将意味着相互排斥的方法。 You should use CyclicBarrier , a single barrier for all threads and you can achieve this. 您应该对所有线程使用CyclicBarrier ,这是一个屏障,您可以实现这一目标。

  1. Different threads are entering in synchronized block at same time. 不同线程同时进入同步块。

The synchronization mechanism is used to have coordinated access to shared variables in a multi threaded environment. 同步机制用于在多线程环境中协调访问共享变量。 Whenever more than one thread accesses a given state variable, and one of them must write to it, they all must coordinate their access to it using synchronization. 每当有多个线程访问给定的状态变量,并且其中一个必须写入该状态变量时,它们都必须使用同步来协调对其的访问。

You are using synchronized(this) in the run method of the PlayerRun class, which says that only one thread of execution can execute this block of code at any given point in time. 您正在PlayerRun类的run方法中使用PlayerRun synchronized(this) ,它表示只有一个thread of execution可以在任何给定时间点执行此代码块。 As you have created 4 PlayerRun instances and started them to run independently of each other, so they are entering their own synchronized blocks at the same time. 当您创建了4个PlayerRun实例并启动它们彼此独立运行时,它们将同时输入自己的synchronized块。

Again you have defined your count variable as static: 再次将count变量定义为静态:

static volatile int count=0;

you need to use the runtime class of PlayerRun as the monitor, because it is not tied to any specific instance of PlayerRun . 您需要将PlayerRun的运行时类用作监视器,因为它没有绑定到PlayerRun任何特定实例。 So the changes of the count variable should be synchronized on PlayerRun.class : 因此, count变量的更改应在PlayerRun.class上同步:

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

Once a thread completes his work, it should increment the count variable and count=4 current thread should notify the 3 other waiting for, ultimately I want to achieve happens-before relationship. 一旦一个线程完成了他的工作,它应该增加count变量,并且count = 4当前线程应该通知其他3个等待,最终我想实现事前发生的关系。

The methods wait, notify and notifyAll gives a group of threads a way to wait for a specific condition to become true (these all are instance methods). 这些方法wait, notify and notifyAll为一组线程提供了一种等待特定条件变为真的方法(这些都是实例方法)。 When you use wait on some object, it means the current thread of execution will wait for some condition to become true for this object. 当对某个对象使用wait时,这意味着当前执行线程将等待该对象满足某些条件。 Whereas notify and notifyAll are used to notify other threads waiting on the same object. notifynotifyAll用于通知其他在同一对象上等待的线程。 These set of methods are used when multiple threads are working with the same object and the way you have written your code is different from this approach. 当多个线程使用同一个对象并且您编写代码的方式与此方法不同时,将使用这些方法集。

My suggestion is to use one shared counter among all the threads. 我的建议是在所有线程之间使用一个共享counter This you can achieve by defining a count variable in the RunGame class and pass an instance of this to PlayerRun runnables: 您可以通过在RunGame类中定义一个count变量并将其实例传递给PlayerRun可运行对象来PlayerRun

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);
...
}
}

You can change the PlayerRun as 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();
    }
}

Actually Every thread will update the count on its turn, so after count 3 it will become 3, when 4th thread would enters it will increment it to 4 and notify all others to repeat the process. 实际上,每个线程都会轮流更新其计数,因此在计数3之后它将变为3,当第4个线程进入时它将将其递增为4,并通知所有其他线程重复该过程。

The above statement is copied from your comment . 以上声明摘自您的comment And I think this changes the requirement that was there initially, as now you want to repeat the process all over again. 我认为这改变了最初的要求,因为现在您要重新重复该过程。 For this, you need to replace the increaseCount method with two methods, one to make the threads wait if the count < 4 and the other is to notify the waiting threads when the count becomes 4: 对于这一点,你需要更换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();
        }

    }

And change the PlayerRun as 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() {
    while (true) {
    // DO YOUR WORK
    runGame.releaseCount();
    runGame.increaseCount();
    }
    }
}

You don't need to use synchronized keyword inside the run method of PlayerRun . 你并不需要使用synchronized里面的关键字run的方法PlayerRun The synchronized keyword is used to coordinate access to shared variables. synchronized关键字用于协调对共享变量的访问。 You have your shared variable count in RunGame , to which you can provide synchronized access by using synchronized keyword on increaseCount and relaesCount . 你有你的共享变量countRunGame ,对此您可以通过使用提供同步的访问synchronized的关键字increaseCountrelaesCount

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM