首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何修复此争用条件?

如何修复此争用条件?
EN

Stack Overflow用户
提问于 2013-04-28 18:34:02
回答 3查看 3.4K关注 0票数 1

我有一个接受客户机的服务器,它有一个关闭服务器的stop()方法,这导致了一个我想要解决的java.nio.AsynchronousCloseExceptionstop()方法是在另一个线程上调用的,我认为这就是导致竞争条件的原因。

下面是我的代码:

代码语言:javascript
复制
public void run() {
    InetSocketAddress addr = new InetSocketAddress(provider.getConnection(), 12354);
    try {
        server = ServerSocketChannel.open();
        server.configureBlocking(true);
        server.socket().bind(addr);
        parent.setIP(addr.getAddress().getHostAddress().toString());
        password = generatePassword();
        parent.setPassword(password);
        parent.setStatus("Ready.");
    } catch (IOException e) {
        parent.die("Could not start server: " + e.getMessage());
        runner = null;
    }
    while (runner == Thread.currentThread()) {
        try {
            SocketChannel sc = server.accept();
            if (available) {
                session = new ReceiveSession(this, sc, password, addr.getAddress());
                session.start();
                available = false;
            } else {
                new ReceiveBusyHandler(sc).start();
            }
        } catch (IOException e) {
            synchronized (swallowException) {
                if (!swallowException) {
                    parent.showError(e.toString());
                }
                available = true;
            }
        }
    }
}

public void stop() throws IOException {
    synchronized (swallowException) {
        swallowException = true;
        runner = null;
        if (server != null) {
            server.socket().close();
            server.close();
        }

        swallowException = false;
        System.out.println("Server down");
    }
}

(仅供参考,swallowException是一个Boolean,你可以看到我尝试过同步它。)

看起来,在我的服务器循环中的异常处理程序有机会访问swallowException之前,stop()方法将swallowException设置为true,然后返回到false

更新:我引入了一个新的Object作为锁,并使用wait()/notify()来解决我的问题:

代码语言:javascript
复制
public void run() {
        InetSocketAddress addr = new InetSocketAddress(provider.getConnection(), 12354);
        try {
            server = ServerSocketChannel.open();
            server.configureBlocking(true);
            server.socket().bind(addr);
            parent.setIP(addr.getAddress().getHostAddress().toString());
            password = generatePassword();
            parent.setPassword(password);
            parent.setStatus("Ready.");
        } catch (IOException e) {
            parent.die("Could not start server: " + e.getMessage());
            runner = null;
        }
        while (runner == Thread.currentThread()) {
            try {
                SocketChannel sc = server.accept();
                if (available) {
                    session = new ReceiveSession(this, sc, password, addr.getAddress());
                    session.start();
                    available = false;
                } else {
                    new ReceiveBusyHandler(sc).start();
                }
            } catch (IOException e) {
                synchronized (lock) {
                    if (!swallowException) {
                        parent.showError(e.toString());

                    }
                    lock.notify();
                    available = true;
                }
            }
        }
    }

    public void stop() throws IOException {
        synchronized (lock) {
            swallowException = true;
            runner = null;
            if (server != null) {
                server.socket().close();
                server.close();
            }
            while (swallowException) {
                try {
                    lock.wait();
                    swallowException = false;
                } catch (InterruptedException e) {
                }
            }
            //swallowException = false;
            System.out.println("Server down");
        }
    }
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-04-28 18:40:03

在Java中,同步是在对象上完成的,而不是在变量上完成的。在swallowException上同步时,将根据其值(Boolean.TRUEBoolean.FALSE)进行同步。这不是你想要的。您应该在包含swallowException的对象上进行同步。

票数 1
EN

Stack Overflow用户

发布于 2013-04-28 18:39:35

此部件未正确同步:

代码语言:javascript
复制
synchronized (swallowException) {
    swallowException = true;

您正在一个实例(false)上同步,并立即将swallowException引用更改为指向另一个实例(true)。进入stop的下一个线程不会阻塞。

要么在不会被换出的实例上进行同步(这些方法的所有者),要么使用java.util.concurrent中的其他锁定机制。

票数 1
EN

Stack Overflow用户

发布于 2013-04-28 18:56:50

我强烈建议重构你的代码(甚至是你作为更新发布的解决方案),因为它只是不清楚发生了什么。

从您的描述来看,您似乎只是想要一个线程安全的方式来停止您的服务器。我建议这样做,这样你就可以简单地call close() on the ServerSocket,并能够捕获一个SocketException

代码语言:javascript
复制
private boolean cont = true;

// this can safely be called from any thread
public synchronized void stop() {
    cont = false;
    if (server != null) {
       server.socket().close();
    }
}
private synchronized void setContinue(boolean value) {
    cont = value;
}
private synchronized boolean shouldContinue() {
    return cont;
}
private synchronized void openChannel() {
    server = ServerSocketChannel.open();
}

public void run() {
    InetSocketAddress addr = new InetSocketAddress(provider.getConnection(), 12354);
    try {
        openChannel();
        server.configureBlocking(true);
        server.socket().bind(addr);
        parent.setIP(addr.getAddress().getHostAddress().toString());
        password = generatePassword();
        parent.setPassword(password);
        parent.setStatus("Ready.");
    } catch (IOException e) {
        parent.die("Could not start server: " + e.getMessage());
        setContinue(false);
    }

    while (shouldContinue()) {
        try {
            SocketChannel sc = server.accept();
            if (shouldContinue()) {
                if (available) {
                    session = new ReceiveSession(this, sc, password, addr.getAddress());
                    session.start();
                    available = false;
                } else {
                    new ReceiveBusyHandler(sc).start();
                }
            }
        } catch (SocketException se) {
            // normal shutdown from stop()
        } catch (IOException e) {
            parent.showError(e.toString()); 
            available = true;               
        }
    }
    System.out.println("Server down");
}

See more about this technique for stopping a server here

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16261974

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档