簡體   English   中英

從Java中的兩個不同線程並發訪問變量

[英]Accessing variable concurrently from two different threads in java

嘗試從兩個不同的線程訪問變量時出現錯誤。 我正在尋求幫助,因為該幫助與我的代碼有關。 我的問題是,如何同時從兩個不同的線程訪問變量。

我有的:

我有一個包含所有對象的ObjectHandler。 我有一個Client類,它將數據發送到服務器。 我有一個游戲類,正在渲染並跟蹤所有局部變量。

我的客戶類需要訪問Object處理程序以發送給玩家,將子彈發送到服務器,而我的游戲類則需要訪問對象以呈現它們。 我的游戲類和客戶類都有各自的線程。

對象處理程序

public class ObjectHandler implements Serializable{
    private static final long serialVersionUID = 4515552114741927916L;

    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private Lock readLock = readWriteLock.readLock();
    private Lock writeLock = readWriteLock.writeLock();

    //////HANDLERS//////
    private KeyInputHandler keyhandler;
    private CollisionHandler collisionHandler;
    private MouseInputHandler MouseInput;

    private Player player;
    private ArrayList<Entity>objects;

    public ObjectHandler(){
        MouseInput = new MouseInputHandler(this);
        keyhandler = new KeyInputHandler();

        objects = new ArrayList<Entity>();
        player = new Player(40,40,20,20,ObjectID.Player,this);
    }

    public synchronized KeyInputHandler getKeyhandler() {return keyhandler;}
    public synchronized CollisionHandler getCollisionHandler() {return collisionHandler;}
    public synchronized MouseInputHandler getMouseInput() {return MouseInput;}

    public synchronized Player getPlayer() {return player;}
    public synchronized void setPlayer(Player player) {this.player = player;}

    public synchronized ArrayList<Entity> getObjects() {return objects;}
    public synchronized void setObjects(ArrayList<Entity> objects) {this.objects = objects;}

}

客戶類別

 package com.Nickhulsey.network;


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import com.Nickhulsey.game.ObjectID;
import com.Nickhulsey.handlers.ObjectHandler;

public class Client implements Runnable{

    Thread Receive;
    ObjectHandler game;

    private InetAddress ipAddress;
    private Socket socket;
    private ObjectOutputStream outToServer;
    private ObjectInputStream inFromServer;

    private Packet out;
    private Packet in;

    public Client(ObjectHandler game, String ipAddress){
        this.game = game;
        in = new Packet();
        out = new Packet();
        try {
            this.ipAddress = InetAddress.getByName(ipAddress);
            socket = new Socket(ipAddress,9000);
            System.out.println("Started!");
        } catch (UnknownHostException e) {
        } catch (IOException e) {
        }
        Receive = new Thread(this);
        Receive.start();
    }

    public synchronized void run() {

        while(true){
            Send();
             try {
                inFromServer = new ObjectInputStream(socket.getInputStream());
                in = (Packet) inFromServer.readObject();
            } catch (IOException e) {
            } catch (ClassNotFoundException e) {
            }
            //unpack our data
            if(in != null){
                unPack(in);
            }
        }
    }

    public void unPack(Packet in){
        //unpack our connected players IF there is any
        game.getObjects().clear();
        //set in's objects to game's objects
        for(int i = 0; i < in.getObjects().size();i++){
            if(in.getObjects() != null){
                game.getObjects().add(in.getObjects().get(i));
            }
        }
    }

    public void Send(){
        try {
            outToServer = new ObjectOutputStream(socket.getOutputStream());
            out = new Packet();

            //set data for our out package
            out.setPlayer(game.getPlayer());
            out.setObjects(game.getObjects());
            outToServer.writeObject(out);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

和我的游戲課

package com.Nickhulsey.game;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.io.Serializable;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import com.Nickhulsey.handlers.ObjectHandler;
import com.Nickhulsey.network.Client;
import com.Nickhulsey.network.Server;

//THREADS ARE NOT SERIALIZABLE
public class Game extends Canvas implements Runnable, Serializable{
    private static final long serialVersionUID = 8279766339522266301L;

    private Thread thread;
    private Client gameClient;
    private Server gameServer;

    public final int SCALE = 1; 
    public int Server;
    boolean running = false;

    ObjectHandler ObjectHandler;

    public Game(){
        //start the game
        ObjectHandler = new ObjectHandler();

        Server = JOptionPane.showConfirmDialog(null,"Run the server?","Server",JOptionPane.YES_NO_OPTION);

        if(Server == JOptionPane.YES_OPTION){
            gameServer = new Server();
        }else{
            String IpConnect = JOptionPane.showInputDialog("Enter Ip to connect to.(25.156.181.27)");
            gameClient = new Client(ObjectHandler,IpConnect);
        }

    }

    public synchronized void start() {
        if (running) {
            return;
        }
        running = true;
        thread = new Thread(this);
        thread.start();
    }

    public synchronized void run() {
        while (running){
            if(Server != JOptionPane.YES_OPTION){
              long lastTime = System.nanoTime();
              double amountOfTicks = 60;
              double ns = 1000000000.0D / amountOfTicks;
              double delta = 0.0D;
              long timer = System.currentTimeMillis();
              int updates = 0;
              int frames = 0;

              while (running){
                long now = System.nanoTime();
                delta += (now - lastTime) / ns;
                lastTime = now;
                while (delta >= 1){
                  tick();
                  updates++;
                  delta -= 1;
                  render();
                }

                frames++;

                if (System.currentTimeMillis() - timer > 1000L){
                  timer += 1000L;
                  frames = 0;
                  updates = 0;
                }
              }
            }
        }
       //stop();
    }

    public void render() {
        BufferStrategy bs = getBufferStrategy();
        if (bs == null)
        {
          createBufferStrategy(3);
          return;
        }
        Graphics g = bs.getDrawGraphics();

        g.setColor(Color.white);
        g.fillRect(0,0, 5000, 5000);

        for(int i = 0; i < ObjectHandler.getObjects().size(); i++){
            ObjectHandler.getObjects().get(i).Draw(g);
        }

        ObjectHandler.getPlayer().Draw(g);

        g.dispose();
        bs.show();
    }

    public void tick() {
        //for(int i = 0; i < objects.size();i++){objects.get(i).Update(objects);}
        ObjectHandler.getPlayer().Update(ObjectHandler.getObjects());
    }


    public static void main (String [] args){
        JFrame window = new JFrame("A Multiplayer GAME");
        Game game = new Game();

        window.setSize(300,300);
        game.addKeyListener(game.ObjectHandler.getKeyhandler());
        game.addMouseListener(game.ObjectHandler.getMouseInput());
        game.addMouseMotionListener(game.ObjectHandler.getMouseInput());
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setLocationRelativeTo(null);
        window.add(game);
        window.setVisible(true);
        game.start();
    }

    public ObjectHandler getObjectHandler() {return ObjectHandler;}
}

我知道這很多,對不起。 我的問題是,我正在訪問項目符號數組列表並對其進行編輯,而另一個線程正在嘗試訪問它。 我嘗試了同步,並且嘗試使用鎖類。 任何人都可以提出解決方法嗎? 抱歉,如果我沒有提出真正的問題,或者沒有遵循堆棧溢出指導原則,我仍然很新。

編輯:錯誤日志:

Exception in thread "Thread-3" java.util.ConcurrentModificationException
    at java.util.ArrayList.writeObject(ArrayList.java:573)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
    at com.Nickhulsey.network.Client.Send(Client.java:78)
    at com.Nickhulsey.network.Client.run(Client.java:45)
    at java.lang.Thread.run(Thread.java:695)

how do I access a variable at the same time from two different threads?

您已經在執行此操作,這就是為什么您會收到ConcurrentModificationException
要防止該異常,請使用java.util.concurrent的類作為變量,或確保線程專門使用該變量。 在您的代碼中, Game.tick()Client.unpack()/Send()的兩個線程同時使用ObjectHandler -list objects 為防止這種情況發生,請執行以下操作:

  • 將共享鎖對象添加到ObjectHandler public final Object updateLock = new Object;
  • Game.tick() ,使用synchronized(ObjectHandler.updateLock) { ... method code ... }啟動方法
  • Client.unpack()/Send() ,使用synchronized(game.updateLock) { ... method code ... }啟動方法

現在, synchronized關鍵字將確保在任何給定時間僅執行三個同步塊之一(​​因為它們都使用相同的對象進行同步)。

updateLock對象不是絕對必需的,您可以僅使用ObjecHandler對象本身進行同步,但是在這種情況下,通過使用單獨的對象進行鎖定來使同步顯式是一種很好的做法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM