Потоки просто перестают работать Java - PullRequest
0 голосов
/ 02 мая 2018

Я программирую очень простую симуляцию, которая должна изменять цвета поля на доске w x h. Каждое поле на доске - это отдельная нить, которая меняет свой цвет каждые случайные миллисекунды. Дело в том, что через некоторое время он просто перестает меняться. Что-то не так с моим кодом, или это вещь оптимизации, или что-то еще? Это какой-то тупик? (Количество потоков ничего не меняет, просто время после остановки отладочной платы составляет 25x25)

Я также обнаружил, что проблема в функции AverageColor () может быть связана с тем, что без нее она работает нормально (например, со статическим цветом вместо среднего)

public void run() {
    while( true ) {
        try {
            int time;
            double q;
            int newcolor;
            synchronized( gen ) { // gen is a Random (one for the whole program)
                time = gen.nextInt( k ) + k/2; // k is a given parameter, 200 for debugging
                q = gen.nextDouble();
                newcolor = gen.nextInt( 256*256*256 );
            }
            synchronized( this ) { // posting rest of the class below
                wait( time ); // replacing with Thread.sleep( time ) does not fix the problem
                if( q < p ) { // with p probability it changes color to rando
                    me.setBackground( new Color( newcolor ) );
                }
                else { // with p-1 it pick an avarge
                    me.setBackground( averageColor() ); // averageColor takes an avarge of neighbour fields
                }
            }
            //Thread.yield(); // removed it as wrestang said, but it does not change anything
        } catch( InterruptedException e ) {
            e.printStackTrace();
        }
    }
}

Остальные в классе:

public class Field extends Thread {

static Panel panel;
Label me;
Random gen;
int x, y, w, h;
int k;
double p;
Field[][] others;

enum Side {
    up,
    down,
    right,
    left
}

enum ColorPart {
    R,
    G,
    B
}

Field( Panel panel, Random gen, Field[][] others, int x, int y, int k, double p, int w, int h ) {
    this.gen = gen;
    this.others = others;
    this.x = x;
    this.y = y;
    this.k = k;
    this.p = p;
    this.w= w;
    this.h = h;

    me = new Label();
    me.setBackground( new Color( gen.nextInt( 256*256*256 ) ) );
    panel.add( me );

    setDaemon( true );
}

synchronized int getColor( ColorPart c ) {
    switch( c ) {
    case B:
        return me.getBackground().getBlue();
    case G:
        return me.getBackground().getGreen();
    case R:
        return me.getBackground().getRed();
    }
    return 0;
}

synchronized int getSideColor( Side side, ColorPart c ) {
    int a, b;
    switch( side ) {
    case down:
        b = y+1;
        while( b >= h ) b -= h;
        return others[x][b].getColor( c );
    case left:
        a = x-1;
        while( a < 0 ) a += w;
        return others[a][y].getColor( c );
    case right:
        a = x+1;
        while( a >= w ) a -= w;
        return others[a][y].getColor( c );
    case up:
        b = y-1;
        while( b < 0 ) b += h;
        return others[x][b].getColor( c );
    }
    return 0;
}

synchronized Color averageColor() {
    int r = 0;
    int g = 0;
    int b = 0;

    r += getSideColor( Side.up, ColorPart.R );
    r += getSideColor( Side.down, ColorPart.R );
    r += getSideColor( Side.right, ColorPart.R );
    r += getSideColor( Side.left, ColorPart.R );
    r /= 4;

    g += getSideColor( Side.up, ColorPart.G );
    g += getSideColor( Side.down, ColorPart.G );
    g += getSideColor( Side.right, ColorPart.G );
    g += getSideColor( Side.left, ColorPart.G );
    g /= 4;

    b += getSideColor( Side.up, ColorPart.B );
    b += getSideColor( Side.down, ColorPart.B );
    b += getSideColor( Side.right, ColorPart.B );
    b += getSideColor( Side.left, ColorPart.B );
    b /= 4;

    return new Color( r, g, b );
}

@Override
public void run() {
    while( true ) {
        try {
            int time;
            double q;
            int newcolor;
            synchronized( gen ) {
                time = gen.nextInt( k ) + k/2;
                q = gen.nextDouble();
                newcolor = gen.nextInt( 256*256*256 );
            }
            synchronized( this ) {
                wait( time );
                if( q < p ) {
                    me.setBackground( new Color( newcolor ) );
                }
                else {
                    me.setBackground( averageColor() );
                }
            }
        } catch( InterruptedException e ) {
            e.printStackTrace();
        }
    }
}

}

Создано и начато с:

public class SymPanel extends Panel {
private static final long serialVersionUID = 1L;

Random gen;
int w, h;
int k;
double p;
Field[][] fields;

SymPanel( int w, int h, int k, double p ) {
    this.w = w;
    this.h = h;
    this.k = k;
    this.p = p;

    setLayout( new GridLayout( h, w ) );

    gen = new Random();

    Field.panel = this;

    fields = new Field[w][h];
    for( int j = 0; j<h; j++ ) {
        for( int i = 0; i<w; i++ ) {
            fields[i][j] = new Field( this, gen, fields, i, j, k, p, w, h );
        }
    }
}

public synchronized void start() {
    for( int j = 0; j<h; j++ ) {
        for( int i = 0; i<w; i++ ) {
            fields[i][j].start();
        }
    }
}

}

В основном:

public static void main( String[] args ) {
    frame = new Frame("Sym");
    frame.setBounds( 300, 100, 1024, 768 );
    frame.addWindowListener( new Sym() );

    sympanel = new SymPanel( 25, 25, 200, 0.3 );
    frame.add( sympanel );

    frame.setVisible( true );

    sympanel.start();
}

Ответы [ 4 ]

0 голосов
/ 03 мая 2018

То, что вы получаете, является типичным тупиком.

Рассмотрим случай двух Field s A при x = 0, y = 0 и B при x = 1, y = 0.

В какой-то момент времени Field A пытается вычислить averageColor() - в течение этого времени он удерживает блокировку на себе (поскольку averageColor() синхронизируется). Часть этого вычисления вызывает getColor(..) на Field B, который синхронизируется на Field B.

Если в то же время Field B пытается вычислить свой averageColor(), Field B удерживает блокировку на себе (поэтому Field A не может вызвать getColor(..) для Field B). Часть этого вычисления, в свою очередь, вызывает getColor(..) на Field A - который блокирует, поскольку Field A удерживает блокировку на себе и пытается вызвать getColor(..) на Field B.

0 голосов
/ 02 мая 2018

В общем, Swing не является потокобезопасным. Любые вызовы методов компонента Swing должны выполняться в потоке диспетчеризации событий. Вы можете прочитать об этом в Учебниках Java «Поток рассылки событий»: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

0 голосов
/ 03 мая 2018

Благодаря @ cmosher01

Удаление synchronized из getColor getSideColor и averageColor решило проблему, но я не совсем понял, почему. Если кто-нибудь знает, я был бы признателен за некоторые объяснения, почему это могло вызвать проблемы.

0 голосов
/ 02 мая 2018

Просто пара вещей:

A. Когда q в вашем коде выше превышает (или соответствует) значение p, код будет вычислять средний цвет - который будет одинаковым каждый раз, при условии, что соседние поля не изменили цвет.

B. Исключение, которое приводит к выходу из цикла run. Я сомневаюсь, что это так, но вы можете перехватывать все ошибки во время отладки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...