Java 如何阻止一个线程修改另一个线程正在使用的数组?

Java 如何阻止一个线程修改另一个线程正在使用的数组?,java,multithreading,swing,thread-safety,Java,Multithreading,Swing,Thread Safety,我有一个java程序,基本上是一个游戏。它有一个名为“世界”的类。“World”类有一个方法“levelChanger()”,另一个方法“makeColorArray()” public class World { private BufferedImage map, map1, map2, map3; private Color[][] colorArray; public World(int scrWd, int scrHi) { try {

我有一个java程序,基本上是一个游戏。它有一个名为“世界”的类。“World”类有一个方法“levelChanger()”,另一个方法“makeColorArray()”

public class World {

    private BufferedImage map, map1, map2, map3;
    private Color[][] colorArray;

    public World(int scrWd, int scrHi) {
        try {
            map1 = ImageIO.read(new File("map1.png"));
            map2 = ImageIO.read(new File("map2.png"));
            map3 = ImageIO.read(new File("map3.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        map = map1;

        makeColorArray();
    }

    private void makeColorArray() {
        colorArray = new Color[mapHi][mapWd]; // resetting the color-array
        for(int i = 0; i < mapHi; i++) {
            for(int j = 0; j < mapWd; j++) {
                colorArray[i][j] = new Color(map.getRGB(j, i));
            }
        }
    }

    //color-array used by paint to paint the world
    public void paint(Graphics2D g2d, float camX, float camY) {
        for(int i = 0; i < mapHi; i++) {
            for(int j = 0; j < mapWd; j++) {
                if(colorArray[i][j].getRed() == 38 && colorArray[i][j].getGreen() == 127 && colorArray[i][j].getBlue() == 0) {
                    //draw Image 1
                }
                else if(colorArray[i][j].getRed() == 255 && colorArray[i][j].getGreen() == 0 && colorArray[i][j].getBlue() == 0) {
                    //draw Image 2
                }
            }
        }
    }

    public void levelChanger(Player player, Enemies enemies) {
        if(player.getRec().intersects(checkPoint[0])) {
            map = map2;
            //calls the color-array maker
            makeColorArray();           
        }
        else if(player.getRec().intersects(checkPoint[1])) {
            map = map3;
            makeColorArray();
        }
    }

    public void update(Player player, Enemies enemies) {
        levelChanger(player, enemies);
    }   
}
公共类世界{
私有缓冲区映像映射,map1,map2,map3;
专用颜色[]彩色阵列;
公共世界(国际scrWd、国际scrHi){
试一试{
map1=ImageIO.read(新文件(“map1.png”);
map2=ImageIO.read(新文件(“map2.png”);
map3=ImageIO.read(新文件(“map3.png”);
}捕获(IOE异常){
e、 printStackTrace();
}
map=map1;
makeColorArray();
}
私有void makeColorArray(){
colorArray=新颜色[mapHi][mapWd];//重置颜色数组
对于(int i=0;i
makeColorArray()方法生成“Color”类型的2d数组。此数组存储PNG图像中的颜色对象。JPanel的paint()方法使用此数组在屏幕上绘制世界

levelChanger()方法用于在某些编码为true时更改游戏的级别(阶段)。该方法调用makeColorArray()
方法,在更改游戏级别时重新生成颜色数组

问题是我有一个在线程上运行的游戏循环。现在,像JPanel这样的swing组件的绘制由java在不同的背景线程上完成。更改游戏级别时,将重新生成颜色阵列对象。现在,正如我前面所说,paint()方法使用color array对象在屏幕上绘制世界。有时,当根据游戏逻辑重新创建颜色数组对象并将其成员设置为null时,背景线程仍在使用颜色数组对象(未完成绘制)。这会导致空指针异常,只是有时会发生。显然这是一种竞赛条件


我想知道如何阻止我的游戏线程重置颜色数组,直到背景摆动线程完成绘制

一种更改最少的方法是同步对颜色数组的访问

我个人会将共享数据抽象成一个完全线程安全的单独类,这样您就不必确保代码库的两个独立部分都必须知道在什么基础上进行同步(乍一看,这里的类似乎不仅仅处理地图,也许我正在描述的就是这样一个类)

private void makeColorArray(){
Color[][]colorArrayTemp=新颜色[mapHi][mapWd];//重置颜色数组
对于(int i=0;i
如果您一次只想修改一个线程
colorArray
,请将其设置为
同步

同步的目的是,它需要一个线程来获得对象的锁。在对象被锁定时尝试访问该对象的任何其他线程都将被阻止(它将等待)

请参阅此帖子:

建议:

  • 为您的程序使用模型视图控件设计,或使用许多类似变体中的一种
  • 使用Swing计时器来驱动GUI游戏循环,但在模型中使用实时片段(而不是计时器的延迟)来确定循环步骤之间的时间长度
  • 模型应该在GUI Swing事件线程中运行
  • 但是它的长期运行任务应该使用SwingWorker在后台线程中运行
  • 这是关键:在后台线程完成对模型的处理之前,不要更新模型的数据,即JPanel用于绘制的数据。PropertyChangeListener和SwingPropertyChangeSupport对此非常有用
  • 确保JPanel在其
    paintComponent(…)
    方法中绘制,而不是在其
    paint(…)
    方法中绘制,并且调用
    private void makeColorArray() {
        Color[][] colorArrayTemp = new Color[mapHi][mapWd]; // resetting the color-array
        for(int i = 0; i < mapHi; i++) {
            for(int j = 0; j < mapWd; j++) {
                colorArrayTemp [i][j] = new Color(map.getRGB(j, i));
            }
        }
        synchronized(colorArray)
        {
             colorArray = colorArrayTemp;
        }
    }
    
    //color-array used by paint to paint the world
    public void paint(Graphics2D g2d, float camX, float camY) {
        synchronized(colorArray)
        {
            for(int i = 0; i < mapHi; i++) {
                for(int j = 0; j < mapWd; j++) {
                    if(colorArray[i][j].getRed() == 38 && colorArray[i][j].getGreen() == 127 && colorArray[i][j].getBlue() == 0) {
                        //draw Image 1
                    }
                    else if(colorArray[i][j].getRed() == 255 && colorArray[i][j].getGreen() == 0 && colorArray[i][j].getBlue() == 0) {
                        //draw Image 2
                    }
                }
            }
    
        }
    }