- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我对 Java 总体来说相当陌生,特别是并发编程,所以如果这是一个新手问题,请原谅我。
我有一个线程(服务器),它管理子线程的集合(每个线程代表客户端和服务器之间的 session )。服务器维护 session 集合,当 session 结束时,它会向父服务器发出已完成的信号,以便服务器可以将其从 session 集合中删除。
有人告诉我,如果您打算将 ArrayList 与线程一起使用,则需要对其进行保护,并且除非同步,否则 int 也可能会出现问题,因此使用两者的方法都是同步的。
接下来是服务器和 session 对象的相关部分。
public class Server {
private int listenPort = 0;
private ServerSocket serverSocket = null;
private List<Session> sessions = new ArrayList ();
private int lastId = 0;
/**
* Start listening for clients to process
*
* @throws IOException
* @todo Maintain a collection of Clients so we can send global messages
* @todo Provide an escape condition for the loop
*/
synchronized public void run () throws IOException {
Session newSession;
// Client listen loop
while (true) {
//int sessionId = this.Sessions.
newSession = this.initSession (++this.lastId);
this.sessions.add (newSession);
//this.Sessions.add (newSession);
new Thread (newSession).start ();
}
}
/**
*
* @return
* @throws IOException
*/
public Socket accept () throws IOException {
return this.getSocket().accept ();
}
/**
*
* @param closedSession
*/
synchronized public void cleanupSession (Session closedSession) {
this.sessions.remove (closedSession);
}
}
这是 session 类:
public class Session implements Runnable {
private Socket clientSocket = null;
private Server server = null;
private int sessionId = 0;
/**
* Run the session input/output loop
*/
@Override
public void run () {
CharSequence inputBuffer, outputBuffer;
BufferedReader inReader;
try {
this.sendMessageToClient ("Hello, you are client " + this.sessionId);
inReader = new BufferedReader (new InputStreamReader (this.clientSocket.getInputStream (), "UTF8"));
do {
// Parse whatever was in the input buffer
inputBuffer = this.requestParser.parseRequest (inReader);
System.out.println ("Input message was: " + inputBuffer);
// Generate a response for the input
outputBuffer = this.responder.respond (inputBuffer);
System.out.println ("Output message will be: " + outputBuffer);
// Output to client
this.sendMessageToClient (outputBuffer.toString ());
} while (!"QUIT".equals (inputBuffer.toString ()));
} catch (IOException e) {
Logger.getLogger (Session.class.getName ()).log (Level.SEVERE, null, e);
} finally {
this.cleanupClient ();
}
}
/**
* Terminate the client connection
*/
public void cleanupClient () {
try {
this.streamWriter = null;
this.clientSocket.close ();
this.server.cleanupSession (this);
} catch (IOException e) {
Logger.getLogger (Session.class.getName ()).log (Level.SEVERE, null, e);
}
}
/**
*
* @param clientSocket
*/
public Session (Server owner, int sessionId) throws IOException {
System.out.println ("Class " + this.getClass () + " created");
this.server = owner;
this.sessionId = sessionId;
this.clientSocket = this.server.accept ();
System.out.println ("Session ID is " + this.sessionId);
}
}
我遇到的问题出在 session 的 CleanupClient 方法中。当服务器中的 CleanupSession 方法标记为 Synchronized 时, session 线程似乎不会终止。相反,根据 Netbeans 的说法,它们会进入一种名为“On Monitor”的状态。
我试图找出这意味着什么以及如何处理它,但没有帮助。我确实发现监视器就像一个只能被单个线程占用的空间,其他线程必须等待轮到才能使用它,这就是Java中实现并发的方式。然而,我找不到解释,为什么子线程调用父类中的同步方法会触发线程明显永久进入这种状态,或者如何处理它。
我确实发现,如果 Server 类中的 cleanupSession 方法未标记为同步,那么线程会按照我的预期终止。但是,如果我需要同步以维护线程安全,那么我不能只让方法保持不同步并相信运气。
我显然错过了一些基本的东西,但我不确定是什么。如果有人能指出我在这里做错了什么,我将不胜感激。
(附录:我希望我应该使用其他一些 Collection 类,而不是 ArrayList,并且知道它是什么对于解决这种特殊情况肯定会很有帮助,但我也希望获得关于如何在唯一可用选项是同步的一般情况下避免此问题的反馈)
最佳答案
正如 Antimony 已经指出的那样,您会遇到死锁,因为 Server 的两种方法在同一对象(即 Server
实例)上同步,并且 run()
方法永远不会释放锁。
另一方面,您仍然需要某种线程间同步来正确更新 session
列表(如果没有同步,您会遇到两个问题:缺乏更改可见性和数据竞争)。
因此,一种解决方案是仅同步尽可能小的代码部分:对 session
的访问(您不需要在任何地方使用this.
,仅在本地名称遮盖实例变量名称的情况下):
...
public void run () throws IOException {
Session newSession;
// Client listen loop
while (true) {
...
newSession = initSession (++lastId);
synchronized (this) {
sessions.add (newSession);
}
...
}
}
public void cleanupSession (Session closedSession) {
synchronized (this) {
sessions.remove (closedSession);
}
}
您认为 List
在这里不是最合适的,您需要 HashMap
来代替,因为您所做的只是添加新客户端并搜索客户端,并且客户端在集合中存储的顺序并不重要(即使它很重要,最好使用一些有序的 Map
,例如 TreeMap
,以提高性能)。因此,您可以将 Server
代码更改为:
private Map<Integer, Session> sessions = new HashMap<IntegerPatternConverter, Session>();
...
// Client listen loop
while (true) {
int key = ++lastId;
newSession = initSession (key);
synchronized (this) {
sessions.put (key, newSession);
}
new Thread (newSession).start ();
}
...
public void cleanupSession (int closedSessionKey) {
synchronized (this) {
sessions.remove (closedSessionKey);
}
}
进行此更改后,您可以通过使用具有内置同步功能的 Map
来完全摆脱 synchronized
:ConcurrentHashMap
。
但是,最好在您掌握了 Java 并发编程的基础知识之后再进行此操作。为此,实践中的 Java 并发是一个很好的起点。我读过的最好的 Java 入门书籍(其中有关于并发性的精彩部分)是 Gosling 和 Holmes 所著的《Java 编程语言》。
关于java - 父子线程同步(子线程 "on monitor"),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15735698/
我有这个 html 代码: HELLO WORLD! X V HELLO WORLD! X V 我想按 X(类关闭)将父 div 的高度更改为 20px 并显示 V(类打开),但在每个 d
在会计应用程序的许多不同实现中,有两种主要的数据库设计方法来保存日志和分类帐数据。 只保留 Journal 信息,然后 Ledger 只是 Journal 的一个 View (因为 journal 总
我想在另一个子里面有一个子, sub a { sub b { } } 我想为每次调用 sub b 创建一个新的 sub a 实例。有没有办法在 Perl 中做到这一点? 当我运行上面的
我有一些代码正在查找重复项并突出显示单元格: Private Sub cmdDups_Click() Dim Rng As Range Dim cel As Range Set Rng = ThisW
可能有一个简单的解决方案,但我很难过。 我有一个包含一个 ID 字段的主表。在两个可能的字段中有一个具有该 ID 的子表。想象一个由选手 A 和选手 B 组成的 double 队。Master 表将有
假设我有一个包含对象的数组: [ { "id": "5a97e047f826a0111b754beb", "name": "Hogwarts", "parentId": "
我正在尝试对 MySQL 数据库表执行一对父/子模型的批量插入,但似乎无法使用标准的 ActiveRecord 功能来完成。所以,我尝试了 activerecord-import gem,但它也不支持
我有一个带有多个子类的父抽象类。最终,我希望通过 GUI 中的进度条显示子类中完成的进度。 我目前所做的,我意识到这是行不通的,是在父类中声明为每个子类将覆盖的虚拟方法的事件方法定义。所以像: pub
是否可以通过键数组在对象中设置变量?例如我有这个对象: var obj = {'outer': {'inner': 'value'} }; 并希望设置由键数组选择的值: var keys = ['ou
我有一个名为 companies 的 MySQL 表,如下所示: +---------+-----------+-----------+ | id_comp | comp_name | id_pare
我正在尝试使用 sublime text 在 sublime text 上的 ionic 上打开我的第一个应用程序。它给了我一个“找不到命令”的错误。如何修复? 我试过这些命令: sudo rm -r
不好意思问,但我正在使用 webapp2,我正在设计一个解决方案,以便更容易定义路由 based on this google webapp2 route function .但这完全取决于能够在子级
我有代表树的数字字符串(我不知道是否有官方名称): 012323301212 上面的例子代表了 2 棵树。根用 0 表示。根的直接子代为“1”,“1”的直接子代为“2”,依此类推。我需要将它们分组到由
是否可以在当前 Activity 之上添加 Activity 。例如,假设我单击一个按钮,然后它将第二个 Activity 添加到当前 Activity 。而第二个 Activity 只覆盖了我当前
我很难思考如何为子资源建模。 以作者的书籍为例。你可以有 N 本书,每本书只有一位作者。 /books GET /books POST /books/id PUT /books/id DELETE 到
有人可以向我解释以下内容(python 2.7) 来自已解析文件的两个字符串数字: '410.9''410.9 '(注意尾随空格) A_LIST = ['410.9 '] '410.9' in '41
背景 在 PowerShell 中构建 hash table 是很常见的通过特定属性快速访问对象,例如以 LastName 为基础建立索引: $List = ConvertFrom-Csv @' I
我真的很难弄清楚如何调用嵌套 Polymer Web 组件的函数。 这是标记: rise-distribution组件有 canPlay我想从 rise-playlist
我写了一个小工具转储(以 dot 格式)一个项目的依赖关系图,其中所有位于同一目录中的文件都聚集在一个集群中。当我尝试生成包含相应图形的 pdf 时,dot开始哭: 命令 dot -Tpdf trim
给定一个 CODE ref,是否可以: 访问该 CODE ref 的解析树 通过指定 CODE ref 的解析树来创建一个新的 CODE ref,该解析树可以包含在 1 中返回的解析树的元素 通常我们
我是一名优秀的程序员,十分优秀!