Параллельные сокеты Java: невозможность совместного использования переменной между потоками - PullRequest
0 голосов
/ 19 декабря 2018

У меня возникает проблема с некоторыми попытками чтения одной и той же переменной в многопоточности сокетов, когда я не могу поделиться ею между потоками.

Это работает как приложение, где работодатель назначает работу дляработник.Через свой интерфейс работодатель может добавлять и присваивать ArrayList внутри класса с именем ListadoPedidos.

Когда ServerSocket работодателя принимает сокет сотрудника, он запускает TCP-соединение и запускает следующий поток:

public class HiloServer implements Runnable{

	private ListadoPedidos peds=new ListadoPedidos();
	private ListadoOperarios operarios=new ListadoOperarios();
	private ListadoSockets sockets=new ListadoSockets();
	private SocketServer s;
	
	public HiloServer(SocketServer sock, JFrame frame, ListadoPedidos pedidos) {
		s=sock;
		peds=pedidos;
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		boolean agregar;
		Socket nuevo;
		try {
			while(true) {
				// ACEPTA OPERARIOS QUE DESEEN CONECTARSE
				s.aceptar();
				nuevo=s.getSocket();
				sockets.addSocket(nuevo);
				new NuevoCliente();
				HiloDatos hd=new HiloDatos(s, nuevo,operarios,peds,sockets);
				Thread t=new Thread(hd);
				t.start();
			}
		}
	   catch (IOException e) {
			e.printStackTrace();
		}
		
	}
}

* обратите внимание, что я отправляю объект, в котором хранятся добавленные добавления.

Затем он запускает другой поток, который будет работать как своего рода «проверка»для номера сотруднику приходится вставлять и отправлять через интерфейс Swing, чтобы действительно войти в систему.Этот поток создается каждый раз, когда новый сотрудник сокета устанавливает TCP-соединение с работодателем ServerSocket.Это идет как:

public class HiloDatos implements Runnable {

	private int n;
	private Socket cliente;
	private SocketServer server;
	private int opRecibido;
	private ListadoOperarios ops;
	private ListadoPedidos peds;
	private ListadoSockets socks;
	
	public HiloDatos(SocketServer ss, Socket nuevo, ListadoOperarios operarios, ListadoPedidos pedidos, ListadoSockets sockets) {
		cliente=nuevo;
		server=ss;
		ops=operarios;
		peds=pedidos;
		socks=sockets;
	}

	@Override
	public void run() {
		server.setSocket(cliente);
		boolean agregar, aceptado=false;
		try {
			do {
				// RECIBE EL NRO OPERARIO Y VERIFICA SU EXISTENCIA
				agregar=true;
				opRecibido=Integer.parseInt(server.recibir()); 
				for(int c=0;c<ops.getOperarios().size();c++) {
					if (opRecibido==ops.getOperarios().get(c)) {
						new ErrorRepetido();
						agregar=false;break;
					}
				}
				if (agregar==true) {
					ops.addOperarios(opRecibido);
					server.enviar("Si");
					aceptado=true;
				}
				}while(aceptado==false);
				HiloPedidos hp=new HiloPedidos(server,opRecibido,ops,peds,socks);
				Thread t=new Thread(hp);
				t.start();
	
				}catch (NumberFormatException e) {
					new ErrorDatos();
				} catch (ConnectException e) {
					new ErrorConexion();
				} catch (SocketException e) {
					try {
						socks.getSockets().remove(socks.getSockets().indexOf(cliente));
						cliente.close();
					} catch (IOException e1) {
						new ErrorFlujo();
					}
					new WarnSocket();
				} catch (IOException e) {
					try {
						socks.getSockets().remove(socks.getSockets().indexOf(cliente));
						cliente.close();
					} catch (IOException e1) {
						new ErrorFlujo();
					}
					new WarnFlujo();
				}
			}
		}

И, наконец, он запускает еще один поток, который ищет тот же номер проверки из потока выше в ArrayList присвоений ("pedidos" класса ListadoPedidos), от которого я продолжал передаватьпоток в поток, и если он находит «новый», он должен отправить его в подключенный сокет:

public class HiloPedidos implements Runnable {

	private Pedido ped;
	private SocketServer server;
	private int op;
	private ListadoOperarios ops;
	private ListadoPedidos peds;
	private ListadoSockets socks;
	
	public HiloPedidos(SocketServer ss, int opRecibido, ListadoOperarios operarios, ListadoPedidos pedidos, ListadoSockets sockets) {
		server=ss;
		opRecibido=op;	
		ops=operarios;
		peds=pedidos;
		socks=sockets;
	}

	@Override
	public void run() {
		int cambio=0, nuevo;
		Pedido pedRecibido;
		try {
			while(true) {
				// ENVÍA PEDIDOS
				nuevo=peds.Contar(op);
				if(nuevo==cambio) {
					cambio=peds.Contar(op);
					pedRecibido=peds.TraerNuevo(op, cambio);
					server.enviarObjeto(pedRecibido);
				}
			}}
				catch (NumberFormatException e) {
					new ErrorDatos();
				} catch (ConnectException e) {
					new ErrorConexion();
				} catch (SocketException e) {
					try {
						socks.getSockets().remove(socks.getSockets().indexOf(server.getSocket()));
						server.getSocket().close();
					} catch (IOException e1) {
						new ErrorFlujo();
					}
					new WarnSocket();
				} catch (IOException e) {
					try {
						socks.getSockets().remove(socks.getSockets().indexOf(server.getSocket()));
						server.getSocket().close();
					} catch (IOException e1) {
						new ErrorFlujo();
					}
					new WarnFlujo();
				}
			}
		}

Проблема в том, что последний поток не может действительно заметить изменение в списке, так как я отлаживал его и никогда не достигал точки останова внутри условия отправки назначения.Класс ListadoPedidos выглядит следующим образом:

public class ListadoPedidos {
	
	private static volatile ArrayList<Pedido> pedidos=new ArrayList<>();
	
	public ListadoPedidos() {
		
	}

	public ArrayList<Pedido> getPedidos() {
		return pedidos;
	}

	public synchronized void addPedidos(Pedido pedido) {
		pedidos.add(pedido);
	}
	
	public int Contar(int o) {
		int n=0;
		for (Pedido p: pedidos) {
			if (p.getNro_operario()==o) {
				n++;
			}
		}
		return n;
	}
	
	
	public Pedido TraerNuevo(int o, int c) {
		int n=0;
		Pedido nuevo = new Pedido();
		for (Pedido p: pedidos) {
			if (p.getNro_operario()==o) {
				n++;
			}
			if (n==c) {
				nuevo=p;break;
			}
		}
		return nuevo;
	}
	
	
}

Contar - это тот, который учитывает назначение со значением nrooperario, равным значению, которое он приносит из потока, а TraerNuevo приносит назначение, которое должно быть отправлено (никогда не достигалось этогометод).

Я пытался объявить ArrayList как volatile и все, но ничего не работает.Имейте в виду, что даже если я использую сокетные соединения, проблема больше связана с общим varaible, который не может обновляться между потоками.Любая помощь будет оценена.

Ответы [ 2 ]

0 голосов
/ 23 декабря 2018

Попробуйте, в основном, синхронизировать доступ.

public class ListadoPedidos {

private static volatile ArrayList<Pedido> pedidos=new ArrayList<>();

public ListadoPedidos() {

}
/**
 * Here DO NOT return the arrayList. The underlying implementation is not threadsafe
 */
//    public  ArrayList<Pedido> getPedidos() {
//        return pedidos;
//    }

public synchronized void addPedidos(Pedido pedido) {
    pedidos.add(pedido);
}

public synchronized int Contar(int o) {
    int n=0;
    for (Pedido p: pedidos) {
        if (p.getNro_operario()==o) {
            n++;
        }
    }
    return n;
}


public synchronized Pedido TraerNuevo(int o, int c) {
    int n=0;
    Pedido nuevo = new Pedido();
    for (Pedido p: pedidos) {
        if (p.getNro_operario()==o) {
            n++;
        }
        if (n==c) {
            nuevo=p;break;
        }
    }
    return nuevo;
}

}
0 голосов
/ 23 декабря 2018

Количество кода, которое вы нам дали, затрудняет ответ на ваш вопрос.Если честно, испанский тоже не помогает.Но я могу дать вам общий совет.

Давайте начнем с вопроса.Какой именно вопрос?Из того, что я могу понять, все сводится к тому, "как два потока могут читать одну и ту же переменную?"Даже если это не вопрос, постарайтесь сделать вопрос как можно более понятным для себя.

Затем начните с нового тест-проекта, отдельного от проекта, над которым вы работаете.Напишите минимальный объем кода, который, по вашему мнению, должен работать.Если это не работает, напишите еще меньше кода, который работает (например, используйте статические переменные, чтобы сделать вещи еще проще).Идите вперед и назад, пока не получите код, который может ответить на ваш вопрос.Если вы не можете заставить его работать, сделайте шаг назад и подумайте о сделанных вами предположениях, которые могут оказаться неверными.

Если вы все еще не можете понять это, вернитесь сюда с минимальным количеством кода, которое, по вашему мнению, должноработа и четкий вопрос.

Этот метод «пробного использования минимального кода в тестовом проекте» - это то, что я до сих пор использую после многих лет программирования для решения проблем.Когда я решаю проблему таким образом, я обычно узнаю что-то новое и часто обнаруживаю, что сделал предположение, которое не подтвердилось.

...