java - 为什么我的 boolean 值没有被改变?

标签 java multithreading network-programming

所以我正在尝试创建一个客户端/服务器程序。我想知道我的客户什么时候自动断开连接,所以我设置了一个心跳系统。我的客户端每 6 秒向我的服务器发送一个 ping,如果客户端在总共 30 秒内未发送 ping,则客户端被认为已断开连接并从当前连接列表中删除(我计划为此实现 GUI)。或者至少,这是计划。

连接管理器.java

public class ConnectionManager implements Runnable{

static Socket connection;

private ArrayList<Thread> allConnections;
private ArrayList<Connection> allConnectionList;
private ServerSocket server;
private int id = 0;

public ConnectionManager() {
    allConnections = new ArrayList<Thread>();
    allConnectionList = new ArrayList<Connection>();
}


@Override
public void run() {
    try {
        server = new ServerSocket(5555);
        System.out.println("Server is running!");
        while(true) {
            connection = server.accept();
            Connection a = new Connection(connection, id);
            Runnable runnable = a;
            allConnectionList.add(a);
            allConnections.add(new Thread(runnable));
            allConnections.get(allConnections.size() - 1).start();
            id++;
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public void removeConnection(int id) {
    allConnections.remove(id);
    allConnectionList.remove(id);
}

连接.java

public class Connection implements Runnable {

private Socket a;
public boolean amIActive;
private int id;

public Connection(Socket a, int id) {
    amIActive = true;
    this.a = a;
    this.id = id;
}

public void onConnect() {
    try {
        String TimeStamp = new java.util.Date().toString();
        String formattedAddress = a.getInetAddress().toString().replace("/", "");
        System.out.println("Received connection from: " + formattedAddress + " at " + TimeStamp);
        Runnable runnable = new ConnectionListener(this);
        Thread connectionThread = new Thread(runnable);
        connectionThread.start();
        String returnCode = "Server repsonded to " + a.getInetAddress().toString().replace("/", "") + " at "+ TimeStamp + (char) 13;
        BufferedOutputStream os = new BufferedOutputStream(a.getOutputStream());
        OutputStreamWriter osw = new OutputStreamWriter(os, "US-ASCII");
        osw.write(returnCode);
        osw.flush();

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

@Override
public void run() {
    onConnect();
    System.out.println("We got this far!");
    while(amIActive) {
        whileTrue();
    }
    System.out.println("This code never gets run because we get stuck in the while loop above");
    Main.b.removeConnection(id);
    System.out.println("Connection was closed from " + a.getInetAddress());
}

public void setOffline(boolean state) {
    this.amIActive = state;
}

public void whileTrue() {
}

public Socket getSocket() {
    return a;
}

ConnectionListener.java

public class ConnectionListener implements Runnable{

public Connection myConnection;
public boolean receivedHeartbeat;
public int missedHeartbeats = 0;

public ConnectionListener(Connection a) {
    this.myConnection = a;
}


@Override
public void run() {

    Runnable runnable = new Heartbeat(this);
    Thread thread = new Thread(runnable);
    thread.start();

    while(myConnection.amIActive) {
        try {
            BufferedInputStream is;
            is = new BufferedInputStream(myConnection.getSocket().getInputStream());
            InputStreamReader isr = new InputStreamReader(is);
            StringBuffer process = new StringBuffer();
            int character;

            while((character = isr.read()) != 13) { //GETTING STUCK HERE BECAUSE STUPID.
                if(character == -1) {
                    myConnection.setOffline(true);
                } else { 
                    process.append((char)character);
                }
            }
            handleInput(process);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public void handleInput(StringBuffer process) {
    String messageSent = process.toString();
    if(messageSent.equals("Ping!")) {
        receivedHeartbeat = true;
    }
}

心跳.java

public class Heartbeat implements Runnable{

private ConnectionListener b;

public Heartbeat(ConnectionListener a) {
    b = a;
}

@Override
public void run() {
    while(true) {
        try {
            Thread.sleep(1000);
            if(b.missedHeartbeats > 5) {
                b.myConnection.amIActive = false;
                System.out.println("Setting amIActiveToFalse!");
            }
            if(b.receivedHeartbeat) {
                b.receivedHeartbeat = false;
            } else {
                b.missedHeartbeats++;
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

我的控制台充满了来自 Heartbeat.java 的 System.out.println("Setting amIActiveToFalse!");。但是 Connection.java 中的 while 循环一直在运行。我相信这可能与我的线程有关,但我无法弄清楚。

最佳答案

当您有一个非 volatile 变量时,无法保证一个线程到另一个线程的更改的可见性。特别是,如果 JVM 检测到线程未更改 boolean,它可以将其内联,这意味着您永远不会看到值发生变化。

简单的解决方案是使 boolean 值 volatile 不会被内联,一个线程会看到另一个线程何时更改它。

更多详情 http://vanillajava.blogspot.com/2012/01/demonstrating-when-volatile-is-required.html

关于java - 为什么我的 boolean 值没有被改变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32682617/

相关文章:

java - Swing 应用程序线程被 JNA 锁定

c# - Thread.Sleep(timeout) 和 ManualResetEvent.Wait(timeout) 有什么区别?

java - Android 中的 AsyncTask、线程

networking - TCP URG(紧急数据)是否被确认?

ios - iOS 10.2 中的 MAC 地址

java - 将数学表达式转换为 Java 代码

java - JScrollPane 似乎无法滚动

java - 解析 YAML 字符串时不能使用制表符缩进

java - ThreadLocal 线程安全吗?

c++ - 为什么 C 或 C++ 书籍不包含任何有关网络的内容?