简体   繁体   English

从Java中的两个不同线程并发访问变量

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

I have been getting an error while trying to access a variable from two different threads. 尝试从两个不同的线程访问变量时出现错误。 I'm asking for help because the help will be relevant to my code. 我正在寻求帮助,因为该帮助与我的代码有关。 My question is, how do I access a variable at the same time from two different threads. 我的问题是,如何同时从两个不同的线程访问变量。

What I have: 我有的:

I have a ObjectHandler that holds all of my objects. 我有一个包含所有对象的ObjectHandler。 I have a Client class that is sending data to the server. 我有一个Client类,它将数据发送到服务器。 I have a game class that is rendering and keeping track of all of the local variables. 我有一个游戏类,正在渲染并跟踪所有局部变量。

My client class needs to access the Object handler to send to the player and bullets to the server and my game class needs to access the objects to render them. 我的客户类需要访问Object处理程序以发送给玩家,将子弹发送到服务器,而我的游戏类则需要访问对象以呈现它们。 My game class and my client class each have their own threads. 我的游戏类和客户类都有各自的线程。

Object Handler 对象处理程序

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

}

Client Class 客户类别

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

and my Game class 和我的游戏课

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

I know that this is a lot, sorry. 我知道这很多,对不起。 My issue is that I'm accessing an arraylist of bullets and edit it, while another thread is trying to access it. 我的问题是,我正在访问项目符号数组列表并对其进行编辑,而另一个线程正在尝试访问它。 I have tried synchronized, and I have tried using the lock classes. 我尝试了同步,并且尝试使用锁类。 Can anyone suggest a way around this? 任何人都可以提出解决方法吗? Sorry if I didn't ask a real question or didn't follow the stack overflow guide lines, I'm still really new. 抱歉,如果我没有提出真正的问题,或者没有遵循堆栈溢出指导原则,我仍然很新。

EDIT: Error Log: 编辑:错误日志:

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?

You are already doing that, that is why you get the ConcurrentModificationException . 您已经在执行此操作,这就是为什么您会收到ConcurrentModificationException
To preven the exception, use a class from java.util.concurrent for the variable or make sure the threads use the variable exclusively. 要防止该异常,请使用java.util.concurrent的类作为变量,或确保线程专门使用该变量。 In your code the objects -list in ObjectHandler is used at the same time by two threads in Game.tick() and Client.unpack()/Send() . 在您的代码中, Game.tick()Client.unpack()/Send()的两个线程同时使用ObjectHandler -list objects To prevent this from happing: 为防止这种情况发生,请执行以下操作:

  • add a shared lock object to ObjectHandler public final Object updateLock = new Object; 将共享锁对象添加到ObjectHandler public final Object updateLock = new Object;
  • in Game.tick() start the method with synchronized(ObjectHandler.updateLock) { ... method code ... } Game.tick() ,使用synchronized(ObjectHandler.updateLock) { ... method code ... }启动方法
  • in Client.unpack()/Send() start the methods with synchronized(game.updateLock) { ... method code ... } Client.unpack()/Send() ,使用synchronized(game.updateLock) { ... method code ... }启动方法

The synchronized keyword will now guarantee that only one of the three synchronized blocks is executed at any given time (since they all use the same object to synchronize against). 现在, synchronized关键字将确保在任何给定时间仅执行三个同步块之一(​​因为它们都使用相同的对象进行同步)。

The updateLock object is not strictly necessary, you could just use the ObjecHandler object itself for synchronization, but it is good practice to make synchronization explicit in these kind of cases by using a separate object for locking. updateLock对象不是绝对必需的,您可以仅使用ObjecHandler对象本身进行同步,但是在这种情况下,通过使用单独的对象进行锁定来使同步显式是一种很好的做法。

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

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