Java 并发套接字:无法在线程之间共享变量
Java Concurrent Sockets: not being able to share a variable between threads
我碰巧遇到了一些尝试在套接字多线程中读取相同变量的问题,无法在线程之间共享它。
它作为一个应用程序工作,雇主可以在其中向员工分配工作。通过他的界面,雇主可以在 class 名为 ListadoPedidos.
的 ArrayList 中添加和赋值。
当雇主的 ServerSocket 接受雇员 Socket 时,它启动 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();
}
}
}
*请注意,我发送了存储添加的分配的对象。
然后它启动另一个线程,该线程将作为一种 "validation" 用于员工必须插入并通过他的 Swing 界面发送才能真正进入系统的号码。每当新的套接字雇员与 ServerSocket 雇主建立 TCP 连接时,就会生成此线程。它是这样的:
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" of class ListadoPedidos)中从上面的线程中查找相同的验证编号,我一直在线程之间传递,如果它找到 "new" 一个,它应该将它发送到连接的套接字:
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();
}
}
}
问题是最后一个线程不能真正注意到列表中的变化,因为我调试它并且从未到达发送分配条件内的断点。 class 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,但没有任何效果。请注意,即使我使用套接字连接,问题更多与共享变量无法在线程之间更新有关。任何帮助将不胜感激。
您给我们的代码量太大,很难回答您的问题。老实说,西班牙人也无济于事。但我可以给你一些一般性的建议。
让我们从问题开始。到底是什么问题?据我了解,归结为:"how can two threads read the same variable?"
即使这不是问题,也请尽量让自己清楚地了解问题。
然后从一个与您正在处理的项目分开的新测试项目开始。编写您认为应该工作的最少量代码。如果它不起作用,请编写更少的有效代码(例如,使用静态变量使事情变得更简单)。来来回回,直到你有可以回答你的问题的代码。如果您无法让它发挥作用,请退后一步,想想您所做的可能不正确的假设。
如果您仍然无法解决问题,请返回此处,并提供您认为应该有效的最少代码量和一个明确的问题。
"trying it with minimal code in a test-project"的这种方法,是我编程多年,至今还在用的解决问题的方法。当我以这种方式解决问题时,我通常会学到新东西,并且经常发现我做出的假设并不成立。
试试这个,基本上,同步访问。
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;
}
}
我碰巧遇到了一些尝试在套接字多线程中读取相同变量的问题,无法在线程之间共享它。
它作为一个应用程序工作,雇主可以在其中向员工分配工作。通过他的界面,雇主可以在 class 名为 ListadoPedidos.
的 ArrayList 中添加和赋值。当雇主的 ServerSocket 接受雇员 Socket 时,它启动 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();
}
}
}
*请注意,我发送了存储添加的分配的对象。
然后它启动另一个线程,该线程将作为一种 "validation" 用于员工必须插入并通过他的 Swing 界面发送才能真正进入系统的号码。每当新的套接字雇员与 ServerSocket 雇主建立 TCP 连接时,就会生成此线程。它是这样的:
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" of class ListadoPedidos)中从上面的线程中查找相同的验证编号,我一直在线程之间传递,如果它找到 "new" 一个,它应该将它发送到连接的套接字:
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();
}
}
}
问题是最后一个线程不能真正注意到列表中的变化,因为我调试它并且从未到达发送分配条件内的断点。 class 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,但没有任何效果。请注意,即使我使用套接字连接,问题更多与共享变量无法在线程之间更新有关。任何帮助将不胜感激。
您给我们的代码量太大,很难回答您的问题。老实说,西班牙人也无济于事。但我可以给你一些一般性的建议。
让我们从问题开始。到底是什么问题?据我了解,归结为:"how can two threads read the same variable?" 即使这不是问题,也请尽量让自己清楚地了解问题。
然后从一个与您正在处理的项目分开的新测试项目开始。编写您认为应该工作的最少量代码。如果它不起作用,请编写更少的有效代码(例如,使用静态变量使事情变得更简单)。来来回回,直到你有可以回答你的问题的代码。如果您无法让它发挥作用,请退后一步,想想您所做的可能不正确的假设。
如果您仍然无法解决问题,请返回此处,并提供您认为应该有效的最少代码量和一个明确的问题。
"trying it with minimal code in a test-project"的这种方法,是我编程多年,至今还在用的解决问题的方法。当我以这种方式解决问题时,我通常会学到新东西,并且经常发现我做出的假设并不成立。
试试这个,基本上,同步访问。
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;
}
}