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;
}

}