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

时间:2014-10-15 01:43:10

标签: java multithreading sockets client

我在尝试从两个不同的线程访问变量时遇到错误。我请求帮助,因为帮助与我的代码相关。我的问题是,如何从两个不同的线程同时访问变量。

我有什么:

我有一个包含所有对象的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;}
}

我知道这很多,抱歉。我的问题是我访问子弹的arraylist并编辑它,而另一个线程正在尝试访问它。我尝试过synchronized,我尝试过使用锁类。任何人都可以建议解决这个问题吗?对不起,如果我没有提出真正的问题,或者没有按照堆栈溢出指南行,我还是真的很新。

编辑: 错误日志:

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)

1 个答案:

答案 0 :(得分:2)

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

你已经这样做了,这就是你得到ConcurrentModificationException的原因。
要防止异常,请使用java.util.concurrent中的类作为变量,或确保线程仅使用该变量。在您的代码中,objects中的ObjectHandler - 列表同时由Game.tick()Client.unpack()/Send()中的两个线程使用。为了防止这种情况的发生:

  • 将一个共享锁对象添加到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对象本身进行同步,但最好通过使用单独的对象在这种情况下使同步显式化。锁定。