有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java我的程序一直在创建多个线程,而我只希望创建一个后台线程。如何防止创建其他线程?

我正在Android Studio中创建一个移动风险游戏(客户端),在IntelliJ中创建一个服务器,利用Javasocket和ObjectoStreams发送/接收字符串,在客户端完成轮次时更新每个客户端的游戏状态

我当前的障碍是,当我启动我的应用程序时(当前使用Pixel 2 API R虚拟机),似乎创建了两个风险游戏,或者正在为客户端创建两个线程,第一个线程正在发送正确的风险游戏配置(显示在应用程序GUI中的配置),然后第二个线程是垃圾。以下是我的主要活动的代码:

package com.example.risk;

import 安卓x.appcompat.app.AppCompatActivity;

import 安卓.content.pm.ActivityInfo;
import 安卓.content.res.Configuration;
import 安卓.graphics.Color;
import 安卓.os.Bundle;
import 安卓.view.View;
import 安卓.widget.Button;
import 安卓.widget.TextView;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

import static java.lang.Thread.sleep;

public class MainActivity extends AppCompatActivity {


    RiskGame game; //game instance
    Button next; //next button
    Button Attack; // attack button
    ArrayList<Button> countryButtons; // array list of country buttons
    Random rand;
    TextView army;
    Client client;

        @Override
    protected void onCreate(Bundle savedInstanceState) {
            getSupportActionBar().hide();
            super.onCreate(savedInstanceState); //used by 安卓
            setContentView(R.layout.activity_main);

            army = findViewById(R.id.armyLabel);

            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //force landscape mode

            rand = new Random(); //random object

            countryButtons = new ArrayList<>(); //initialize arrayList

            game = new RiskGame();  //new game instance
            next = findViewById(R.id.nextButt); //next button
            Attack = findViewById(R.id.attackBut); // attack button

            int orentation = getResources().getConfiguration().orientation;
            if (Configuration.ORIENTATION_LANDSCAPE == orentation) {
                for (int i = 0; i < 16; i++) {

                    String name = "c" + (i + 1);
                    Button button = findViewById(getResources().getIdentifier(name, "id", getPackageName()));
                    button.setTag(game.getCountry(i + 1));
                    countryButtons.add(button);

                }

                countryButtonSetUp(countryButtons); //sets up button behavior
                nextButtonSetup();// sets up next button

                Attack.setOnClickListener(v -> { //sets attack button behavior
                    if (game.getPhase() == 2) {
                        game = game.attack();
                    } else if (game.getPhase() == 3) {
                        game = game.move();
                    }
                    updateButtons();
                });

                for (int i = 0; i < 12; i++) {
                    next.performClick();
                }
            } else {

            }

        }

    /**
     * Updates a country button's display features
     */
    public void updateButtons(){
        try {
            for (Button button : this.countryButtons) {
                Country c = (Country) button.getTag();
                button.setTag(game.getCountry(c.getcID()));
                button.setText(c.getcID() + ": " + c.getArmiesHeld()); //sets the text to proper number of armies
                // System.out.println(c.getPlayerNum() + "Player Num"); // delete
                int val = c.getPlayerNum(); //switch statement to set color
                switch (val) {
                    case 1:
                        button.setBackgroundColor(Color.RED);
                        break;
                    case 2:
                        button.setBackgroundColor(Color.WHITE);
                        break;
                    case 3:
                        button.setBackgroundColor(Color.YELLOW);
                        break;
                    case 4:
                        button.setBackgroundColor(Color.BLUE);
                        break;
                    default:
                        break;
                }
                if(game.getPhase() == 1){
                    Attack.setVisibility(View.INVISIBLE);
                }else if(game.getPhase() == 2){
                    Attack.setVisibility(View.VISIBLE);
                    Attack.setText("Attack");
                }else if (game.getPhase() == 3){
                    Attack.setText("Move");
                }

            }
            army.setText(game.getActivePlayer().getPlaceablearmies() + "  ");

        }catch (Exception e){

        }

        //Create the Client object and pass the RiskGame object to it

        }


    /**
     * sets up country button behavior
     * @param buttons
     */
    public void countryButtonSetUp(ArrayList<Button> buttons){

        for(Button button:buttons) { //iterate over each button
            Country c = (Country) button.getTag(); //get country data
            button.setOnClickListener(v ->{ //set on click behavior

                if(game.getPhase() == 1) { //place troop phase of turn
                    if (game.getActivePlayer().getPlayerNum() == c.getPlayerNum()) { //if active player holds country
                       Player active = game.getActivePlayer();
                       if(active.getPlaceablearmies() >0 ) {
                           this.game = game.addArmy(c.getcID());
                           button.setTag(game.getCountry(((Country) button.getTag()).getcID())); //update buttons data
                           active.setPlaceablearmies(active.getPlaceablearmies() -1);
                           updateButtons(); //update display
                       }
                    }
                }else if(game.getPhase() ==2){ // attack phase
                    if(game.getActivePlayer().getPlayerNum() == c.getPlayerNum()){ //player controls country
                        game.setAttackingCount(c); //sets the country as attacking
                    }else { //else set to defending
                        game.setDefendingCount(c);
                    }

                }else if(game.getPhase() == 3){ //move phase

                    //sets countries that are being moved from and to, if reclicked deselect the country
                    if(game.getActivePlayer().getPlayerNum() == c.getPlayerNum()){
                        if(game.getMoveFromcID() == 0){
                            game.setMoveFromcID(c.getcID());
                        }else if(game.getMoveTocID() == 0){
                            game.setMoveTocID(c.getcID());
                        }else if(game.getMoveTocID() == c.getcID()){
                            game.setMoveTocID(0);
                        }else if(game.getMoveFromcID() == c.getcID()){
                            game.setMoveFromcID(0);
                        }
                    }
                }
              });
          //  System.out.println(c.getNeighborCIDs() +"");//delete
            }
        updateButtons(); //update the buttons display
    }
        public void nextButtonSetup(){//change game phase and player
        next.setOnClickListener(v ->{
            game.phaseChange();
            updateButtons();
        });
        }
}

这里最相关的代码是调用RiskGame构造函数的onCreate方法。RiskGame构造函数在onCreate方法中只调用一次。以下是我的RiskGame类,其中创建线程的最相关位置位于构造函数的第83行:

package com.example.risk;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RiskGame implements Serializable {

    Player activePlayer;
    private int activePlayerID;
    int phase;
    ArrayList<Player> players;
    Country attackingCount;
    Country defendingCount;
    ArrayList<Country> countries;
    Random rand;



    int moveFromcID;
    int moveTocID;
    int aDie1;
    int aDie2;
    int aDie3;
    int dDie1;
    int dDie2;

    public RiskGame() {
        players = new ArrayList<Player>();
        for (int i = 1; i < 5; ++i) {
            players.add(new Player(i));
        }
        activePlayer = players.get(0);
        phase = 1;
        attackingCount = new Country();
        defendingCount = new Country();
        countries = new ArrayList<>();
        rand = new Random();
        aDie1 =0;
        aDie2 =0;
        aDie3 =0;
        dDie1 =0;
        dDie2 =0;
        moveFromcID =0;
        moveTocID =0;

        ArrayList<int []> neighbors = new ArrayList<int[]>(); // array of neighbor arrays
        neighbors.add(new int[]{2});
        neighbors.add( new int[]{1, 3, 14, 15});
        neighbors.add(new int[]{2, 4, 16});
        neighbors.add(new int[]{3, 5, 16});
        neighbors.add(new int[]{4, 6, 16});
        neighbors.add(new int[]{5, 7, 8});
        neighbors.add( new int[]{6, 8});
        neighbors.add(new int[]{6, 7, 9, 16, 15});
        neighbors.add(new int[]{8, 10});
        neighbors.add(new int[]{9, 11, 12});
        neighbors.add(new int[]{10});
        neighbors.add( new int[]{10, 13});
        neighbors.add( new int[]{12, 14});
        neighbors.add(new int[]{13, 2});
        neighbors.add( new int[]{2, 8, 16});
        neighbors.add(new int[]{3, 4, 5, 8, 15});


        for(int i = 0; i < neighbors.size(); i++) { //sets up random gameboard
            int j = 0;
            do {
            j = rand.nextInt(4) ;
            }while (players.get(j).getHeldCountries().size() >= 4); //randomly selects a player that doesn't have 4 held countries
            Country c = new Country(j + 1, neighbors.get(i).length+1 , i + 1, neighbors.get(i)); //makes the country
            addCountry(c); //adds to the arraylist
            players.get(j).addCounty(c); //gives country to player
        }

        //Add Client Here

        ExecutorService worker = Executors.newSingleThreadExecutor();
        Client client = null;
        try {
            client = new Client(this);
        } catch (IOException e) {
            e.printStackTrace();
        }
        Client finalClient = client;
        worker.submit(() -> {
            while (!Thread.interrupted())
            finalClient.start();
        });
    }

    public  void addCountry(Country country){
        countries.add(country);
    }
    public Player getActivePlayer() {
        return activePlayer;
    }

    public void setActivePlayer(Player activePlayer) {
        this.activePlayer = activePlayer;
    }

    public ArrayList<Player> getPlayers() {
        return players;
    }

    public void setPlayers(ArrayList<Player> players) {
        this.players = players;
    }

    public RiskGame nextPlayer(){
        if(activePlayer.getPlayerNum() == 4){
            activePlayer = players.get(0);
        }else {
            activePlayer = players.get(activePlayer.getPlayerNum());
        }
        return this;
    }

    public int getPhase() {
        return phase;
    }

    public void setPhase(int phase) {
        this.phase = phase;
    }

    public Country getAttackingCount() {
        return attackingCount;
    }

    public void setAttackingCount(Country attackingCount) {
        this.attackingCount = attackingCount;
    }

    public Country getDefendingCount() {
        return defendingCount;
    }

    public void setDefendingCount(Country defendingCount) {
        this.defendingCount = defendingCount;
    }

    public void setActivePlayerID(int activePlayerID){
        this.activePlayerID = activePlayerID;
    }

    public int getActivePlayerID(){
        return this.activePlayerID;
    }

    public RiskGame attack(){
        if(phase == 2){
            boolean validAttack = false;
            for (int i =0 ; i < attackingCount.getNeighborCIDs().length; i++){
                System.out.println(attackingCount.getNeighborCIDs()[i]);
                if (attackingCount.getNeighborCIDs()[i] == defendingCount.getcID() && attackingCount.getArmiesHeld() >1){
                    validAttack = true;
                }
            }


            if (!validAttack){
                System.out.println("not vaild attack"); //delete
                System.out.println(attackingCount.getcID() + "attack cid" + defendingCount.getcID()); //delete
                System.out.println("attacking cids" + Arrays.asList(attackingCount.getNeighborCIDs()).toString() + " cid: "+ attackingCount.getcID());
               for (int i =0 ; i < attackingCount.getNeighborCIDs().length; i++){
                   System.out.println(attackingCount.getNeighborCIDs()[i]);
               }

                System.out.println("defending cids" + Arrays.asList(defendingCount.getNeighborCIDs()).toString() + " cid: "+ defendingCount.getcID());
                attackingCount = new Country();
                defendingCount = new Country();
            }else {
              rollAttack();
              rollDefence();
              int cId;

              if(aDie1 > dDie1 || aDie2 >dDie1 || aDie3 > aDie1){
                cId  = defendingCount.getcID();
                  countries.get(cId - 1).removeArmy();
              }else if((dDie1 >  aDie1 && dDie1 > aDie2 && dDie1 > aDie3)) {
                   cId = attackingCount.getcID();
                   countries.get(cId - 1).removeArmy();
              }

              if (dDie2 != 0 && attackingCount.getArmiesHeld() > 1){
                    if(aDie1 > dDie2 || aDie2 >dDie2 || aDie3 > aDie2){
                        cId  = defendingCount.getcID();
                        countries.get(cId - 1).removeArmy();
                    }else if((dDie2 > aDie1 &&  dDie2 > aDie2 && dDie2 > aDie3)) {
                        cId = attackingCount.getcID();
                        countries.get(cId - 1).removeArmy();
                  }
              }

              if(defendingCount.getArmiesHeld() <= 0){
                  cId = defendingCount.getcID();
                  countries.get(cId - 1).setPlayerNum(attackingCount.getPlayerNum());
                  countries.get(cId -1).setArmiesHeld(1);

                  cId = attackingCount.getcID();
                  countries.get(cId -1).setArmiesHeld(countries.get(cId -1).getArmiesHeld() - 1);
                  attackingCount = new Country();
                  defendingCount = new Country();
              }
              diceToZero();
              System.out.println( attackingCount.getcID() + " is attacking " + defendingCount.getcID()); //delete
            }

        }
        return this;
    }

    private void diceToZero(){
        aDie1 =0;
        aDie2 =0;
        aDie3 =0;
        dDie1 =0;
        dDie2 =0;
    }

    public RiskGame phaseChange(){
        if(phase != 3){ //if not phase 3 increment phase
            phase+= 1;

        }else { //otherwise set phase to one and change active player
          phase = 1;
          nextPlayer();
          activePlayer.setPlaceablearmies(0);
          for(Country c: countries) {
              if (c.getPlayerNum() == activePlayer.getPlayerNum() ) {
                  activePlayer.setPlaceablearmies(activePlayer.getPlaceablearmies() + c.getArmyValue());
              }
          }
          moveTocID =0;
          moveFromcID =0;
          if(activePlayer.getPlaceablearmies() ==0){
              phaseChange();
              phaseChange();
              phaseChange();
          }

        }
        return this;
    }

    public RiskGame addArmy(int cid){
        countries.get(cid - 1).addArmy();
        return this;
    }


    //Rolls the attacking dice
    private void rollAttack(){
        if(attackingCount.getArmiesHeld() > 3){
            aDie1 = rand.nextInt(7 )+1;
            aDie2 = rand.nextInt(7 )+1;
            aDie3 = rand.nextInt(7 )+1;

        }else if(attackingCount.getArmiesHeld() > 2){
            aDie1 = rand.nextInt(7 )+1;
            aDie2 = rand.nextInt(7 )+1;
        }else {
            aDie1 = rand.nextInt(7 ) +1;
        }
        System.out.println(aDie1 +" " + aDie2 +" " +aDie3);
    }

    //rolls the defence dice
    private void rollDefence(){
        if(defendingCount.getArmiesHeld() > 1){
            dDie1 = rand.nextInt(7 )+1;
            dDie2 = rand.nextInt(7 )+1;
        }else {
            dDie1 = rand.nextInt(7 )+1;
        }
        System.out.println(dDie1 +"  "+ dDie2);
    }

    public RiskGame move(){


        if(phase == 3){
            boolean validMove = false;
            if(moveTocID != 0 && moveFromcID != 0){
                for (int i = 0; i < countries.get(moveFromcID - 1).getNeighborCIDs().length; i++) {
                    if (countries.get(moveFromcID - 1).getNeighborCIDs()[i] == moveTocID && countries.get(moveFromcID - 1).getArmiesHeld() > 1) {
                        validMove = true;
                    }
                }
                if (moveTocID != 0 && moveTocID != moveFromcID && moveFromcID != 0 && validMove) {
                    addArmy(moveTocID);
                    countries.get(moveFromcID - 1).removeArmy();
                }
            }
        }
        moveFromcID =0;
        moveTocID =0;
        return this;
    }

    public Country getCountry(int cid){
        return countries.get(cid - 1);
    }



    public int getMoveFromcID() {
        return moveFromcID;
    }

    public void setMoveFromcID(int moveFromcID) {
        this.moveFromcID = moveFromcID;
    }

    public int getMoveTocID() {
        return moveTocID;
    }

    public void setMoveTocID(int moveTocID) {
        this.moveTocID = moveTocID;
    }
}

下面是我的“客户”类:

package com.example.risk;
import 安卓.util.Pair;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.Duration;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

public class Client extends Thread{

    RiskGame g;
    int ID;
    boolean playerExited = false;
    ReentrantLock lock = new ReentrantLock();
    AtomicBoolean started = new AtomicBoolean(false);

    public Client(RiskGame g) throws IOException {
        this.g = g;
        this.ID = generateID();
    }

    public String writeGameStateToString(){
        String gamestate = "";

        gamestate += this.ID + ":";

        for(Country countries: g.countries){
            gamestate += countries.getcID() + " " + countries.getPlayerNum() + " " + countries.getArmyValue() + ":";
        }

        return gamestate;
    }

    @Override
    public synchronized void run() {
        try {
            lock.lock();
            this.sendData();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public synchronized static int generateID(){
        ZonedDateTime nowZoned = ZonedDateTime.now();
        Instant midnight = nowZoned.toLocalDate().atStartOfDay(nowZoned.getZone()).toInstant();
        Duration duration = Duration.between(midnight, Instant.now());
        long seconds = duration.getSeconds();
        return (int) (seconds%(65535-4999))+4999;
    }

    public synchronized void sendData() throws IOException {
        if(!started.getAndSet(true)) {
            System.out.println(ID);
            Socket sendingSocket = new Socket("10.0.2.2", 4999);
            OutputStream os;
            BufferedOutputStream bos;
            ObjectOutputStream oos;

            os = sendingSocket.getOutputStream();
            bos = new BufferedOutputStream(os);
            oos = new ObjectOutputStream(bos);
            oos.writeObject(writeGameStateToString());

            oos.flush();

            while (!this.playerExited) {
                //Get gamestate back from server
                receiveData();
                os = sendingSocket.getOutputStream();
                bos = new BufferedOutputStream(os);
                oos = new ObjectOutputStream(bos);
                oos.writeObject(writeGameStateToString());

                oos.flush();

            }
            sendingSocket.close();
        }
    }

    public synchronized void receiveData() throws IOException {
        Socket receivingSocket;
        InputStream is;
        BufferedInputStream bis;
        ObjectInputStream ois;
        boolean listening = true;
        while(listening){
            ServerSocket clientListening = new ServerSocket(this.ID);
            receivingSocket = clientListening.accept();
            is = receivingSocket.getInputStream();
            bis = new BufferedInputStream(is);
            ois = new ObjectInputStream(bis);
            try {
                RiskGame temp = (RiskGame) ois.readObject();
                if(temp.getActivePlayerID() == ID) {
                    listening = false;
                    this.g = temp;
                }else{
                    System.out.println(temp.getActivePlayer() + ": Updated local gamestate");
                    this.g = temp;
                }
            } catch (ClassNotFoundException e) {
                System.out.println("Received incompatible Object Type from Server");
            }
            ois.close();
            bis.close();
            is.close();
            clientListening.close();
        }
    }
}

以下是我在服务器端生成的输出示例,其中第一个输出块有效,第二个输出块是完全不同的RiskGame对象,未显示在应用程序上,我不确定它是如何创建的:

Server Output Image Actual Game GUI/Board on App

问题是“用户2已加入”到目前为止应该只有一个用户。我知道这是一个巨大的代码转储,但我想确保任何相关的代码将包括在内。我希望我清楚地说明了哪些部分是最相关的,重新迭代最相关的代码将在第一位代码的onCreate方法中,在第二位代码的底部是RiskGame构造函数,在客户端类的sendData方法中

如果您需要任何信息或有任何问题/建议/批评;)请随意把它们放在下面,我可以得到任何帮助。也很抱歉GUI看起来像垃圾我不是艺术家。哈哈


共 (1) 个答案

  1. # 1 楼答案

    我认为问题可能就在这里

    ExecutorService worker = Executors.newSingleThreadExecutor();
        Client client = null;
        try {
            client = new Client(this);
        } catch (IOException e) {
            e.printStackTrace();
        }
        Client finalClient = client;
        worker.submit(() -> {
            while (!Thread.interrupted())
            finalClient.start();
        });
    

    为什么在提交中使用while循环?如果使用启动方法?它可以让你有更多的线索。不穿线试试。中断后,它将与客户端只生成一个线程