了解java多线程同步

时间:2016-12-07 19:16:19

标签: java multithreading

我无法理解以下代码的输出。 我的理解是输出没有任何特定的顺序,但PlayerX startedPlayerXPlayerX died应该按顺序排列。我们应该让所有玩家都在缓冲区日志中并且应该打印到最后。 但有时候序列是PlayerX startedPlayerX died然后是PlayerX,这些情况下玩家名称不在缓冲区中。有人可以指出我错过了什么吗?

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Game {

    public static void main(String[] args) {
        Ball gameBall = new Ball();
        ExecutorService executor = Executors.newFixedThreadPool(5);
        Player[] players = new Player[50];
        for (int i = 0; i < players.length; i++) {
            Player playerTemp = new Player("Player" + i, gameBall);
            executor.submit(playerTemp);
            players[i] = playerTemp;
            System.out.println(players[i].getName1() + " started");
        }

        for (int i = 0; i < players.length; i++) {

            try {
                players[i].join();
                System.out.println(players[i].getName1() + " died");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        /*
                 * here all thread should die and following line should display
          *all player name 
                 * without any particular order
         and should be last line.
         */

        executor.shutdown();

        System.out.println(gameBall.getLog());
    }

}

...

class Player extends Thread {

    private final String name;
    private final Ball ball;

    public Player(String aName, Ball aBall) {
        name = aName;
        ball = aBall;
    }

    @Override
    public void run() {
        ball.kick(name);
    }

    /**
     * @return the name
     */
    public String getName1() {
        return name;
    }

}

...

class Ball {

    private volatile StringBuffer log;

    public Ball() {
        log = new StringBuffer();
    }

    public synchronized void kick(String aPlayerName) {

        log.append(aPlayerName + " ");
        System.out.println(aPlayerName);

    }

    public String getLog() {
        return log.toString();
    }

}

2 个答案:

答案 0 :(得分:2)

首先关闭:如果您向Thread.sleep(100);方法添加额外的Player.run(),则会大大增加代码行为错误的可能性。

您对多线程的理解实际上是正确的。 但是,由于您从未启动线程players[i].join();,因此您对players[i]的调用没有达到预期的效果。

而是将其提交给ExecutorService。此ExecutorService通过从其现有线程之一调用其Player - 方法来执行run()。在你的情况下,他们是5个执行所有玩家工作的线程。

要获得所需的结果,您有两种可能性:

  1. 请勿使用ExecutorService,而是直接致电start()

    for (int i = 0; i < players.length; i++) {
        Player playerTemp = new Player("Player" + i, gameBall);
        playerTemp.start();
        players[i] = playerTemp;
        System.out.println(players[i].getName1() +" started");
    }
    
  2. 使用executor.awaitTermination(..)代替Thread.join()

    executor.shutdown();
    while(!executor.isTerminated()) {
        try {
            executor.awaitTermination(1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    

答案 1 :(得分:0)

我已经尝试过你的代码了,每当我看到日志中的所有玩家时,只是按照不同的顺序。如果使用ArrayList而不是StringBuffer,则检查日志会更容易,然后您可以打印数组的大小,在我的测试中总是返回50。

在你的代码中你会得到&#34; PlayerX&#34;和#34; PlayerX开始&#34;有时候错误的顺序,因为&#34; PlayerX开始&#34; 线程开始后打印&#34; PlayerX&#34;打印出来。主线程和播放器线程同时运行,因此它们的顺序是不可预测的。

我不知道你怎么能得到&#34; PlayerX死了#34;之前&#34; PlayerX&#34;,我在测试中没有看到这种行为...