Android 上使用 wait/notify 的奇怪 bug

2019-11-27 18:25:30 +08:00
 gramyang
在主线程连接了 socket,在异步的准备工作做好了解除主线程阻塞。

主线程阻塞:
mConnectThread = new ConnectionManagerImpl.ConnectionThread(" Connect thread for " + info);
mConnectThread.setDaemon(true);
mConnectThread.start();
synchronized (mConnectThread) {
try {
mConnectThread.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
子线程初始化成功后解除主线程阻塞:
在子 Thread 的 run()中
synchronized (this) {
this.notifyAll();
}

结果 android 主线程直接无限阻塞了。。。。求大佬支招
4221 次点击
所在节点    Java
17 条回复
Kamiyu0087
2019-11-27 18:38:22 +08:00
把 mConnectThread.start(); 放到 synchronized 块里执行
你这 wait() 的时候有可能 notifyAll() 已经跑过了
gramyang
2019-11-27 20:29:46 +08:00
@Kamiyu0087 mConnectThread.start()不在 synchronized 块里面啊
Aruforce
2019-11-27 20:56:46 +08:00
Synchro 里面的代码块 写 thread.currenthread.wait……你这 Java 真差的可以…
Aruforce
2019-11-27 20:58:39 +08:00
#3

synchronized (mConnectThread) {
try {
Thread.currentThread().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
falsemask
2019-11-27 22:21:39 +08:00
子线程 syn 的 this 是 mConnectThread 对象吗
falsemask
2019-11-27 22:24:02 +08:00
@Aruforce 你要不要试试你写的代码,你 synchronized 获取了 mConnectThread 的锁,然后在 Thread.currentThread()上 wait,你确定不会抛异常?
hhx
2019-11-28 01:07:55 +08:00
简单的线程同步,用初值为 0 的信号量即可,Java 中的 Semaphore。你程序的问题我想一楼的回答是正确的,你自己也说了是异步,走走停停不是一贯到底。
gramyang
2019-11-28 07:31:17 +08:00
@falsemask 是的
gramyang
2019-11-28 07:32:08 +08:00
@falsemask 所以我最烦这种没长眼睛,上来就秀优越感的人,v 站特别多,水平不怎么样还特爱装逼,我都懒得搭理
gramyang
2019-11-28 07:41:48 +08:00
@Kamiyu0087
@hhx
我测试了一下,不行。果然还是我猜测的那样,android 里面的主线程是不能阻塞的,只要手动 wait 就会卡死。。。必须放到子线程里面去才行
Aruforce
2019-11-28 08:09:35 +08:00
@falsemask 抛什么异常? Illegal monitor ?别逗…
GuuJiang
2019-11-28 09:49:17 +08:00
这是一个对 monitor 对象及 wait/notify/notifyAll 等方法的典型误解,要注意这几个方法是定义在 Object 类里的,从逻辑上来说 monitor 对象表示的是你需要控制互斥及同步访问的资源本身,而不是线程,这就是为什么很多代码里会有类似 Object lock = new Object()这样的语句,作为 monitor 的对象只需要跟你想要控制的资源有个一一对应的关系即可,基本不会有把 Thread 对象作为 monitor 的需求

jdk 源码的 Thread.join 方法的注释里有这样一句话

It is recommended that applications not use wait, notify, or notifyAll on Thread instances.
gramyang
2019-11-28 10:37:13 +08:00
@GuuJiang 关键是我现在的需求就是保证一个线程阻塞直至另一个线程执行完后再执行,而 android 的主线程不允许有任何阻塞的操作,至于是用 object 的 wait 还是 condition 的 wait 还是 future 的 get 没有区别。
BrokenVns
2019-11-28 12:40:57 +08:00
1.主线程是可以有阻塞操作的,但不建议。
2.我觉得你的 synchronized (mConnectThread)和 synchronized (this)可能不是同一对象,你这个 this 是 Thread 还是 Runnable ?我建议你把这两个对象打印出来看一下。
3.你可以试试定义额外的对象作为锁来试试,比如都使用 synchronized (lockObject)
gramyang
2019-11-28 17:53:57 +08:00
@BrokenVns mConnectThread 和 this 是同一个对象,this 是 Thread 的子类。synchronized (object)和 synchronized (this)应该是一样的吧?前提是 object 和 this 都是只有一个
BrokenVns
2019-11-28 19:34:11 +08:00
@gramyang 你的 run()是在 ConnectThread 类里直接重写的吗?
我见过有人这么写的:
t = new Thread(()->{
synchronized (this){...}
});
synchronized (t){...}
上面这种情况就把对象混淆了。
如果不是我猜的这种情况,你多加点 log 看看,要不试试第 3 条。
gramyang
2019-11-29 07:33:02 +08:00
@BrokenVns 不是这样的,是在 ConnectThread 里面调用的 this

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/623709

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX