线程通信
最近发现多线程这一块的知识貌似还挺重要的,之前一直没太重视,于是趁着还有些空闲时间赶紧找一套教程好好学习一下,可能是由于太菜吧,有的概念看的云里雾里的。学过的东西又担心会忘掉,所以写一篇博客简单记录一下。
首先是,通过共享对象通信,这个之前做毕设的时候,由于担心演示项目的时候没什么内容可以讲,于是强行将多线程加进去了,看到这一部分的时候,突然回想起当初的思路和这个共享对象的思路好像有些地方不谋而合,不同的线程指向同一个共享实例,既然共享实例可以这样应用,那么静态变量应该也可以实现线程间通信的功能(此处个人猜测,没经过实验)。
接下来是wait(),notify()和notifyAll(),这三个方法之前也有所耳闻,等待、随机唤醒一个线程、唤醒全部线程,而我在网上找到的教程则介绍的更加详细。
当线程调用任意对象的wait方法后,都会进入非运行状态。但是,在调用wait方法之前,必须先获取该对象的锁。也提到了这个语法通常配合同步块一起使用,也就是synchronized。
例如这样用:
//粘过来之后,格式乱掉了
synchronized (lock) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }
当唤醒线程调用相同对象的notify()或notifyAll()方法时,线程会被唤醒(如果有多个线程同时休眠的话,调用notify可能会唤醒这个线程,也可能是其他线程,这是随机的)。
也就是说,等待线程和唤醒线程之间,通过监视器对象来进行通信,那么问题又来了,调用wait方法之前,等待线程已经获取了监视器对象的锁,那么唤醒线程应该无法获取监视器对象的锁才对,那么要如何唤醒线程呢?
关于这一点文章中也给出解释,当线程调用了wait方法,就会释放所持有的监视器对象上的锁。
而且被唤醒的对象必须重新获取监视器对象的锁才能退出wait方法的调用,所以当notifyAll时,同一时刻只有一个线程退出wait方法。
然后是关于丢失信号的部分,看得出来整套教程应该是汉化过来的,读起来的时候感觉有的地方不是那么通顺,不过稍微思考一下,这一部分的内容也并不难理解,如果在wait被调用之前调用了notify,那么就是丢失了信号,这两个方法的调用顺序搞错了的话,可能会导致线程永久等待。所以,我们可以将调用两个方法的记录通过变量记录下来,然后在适当的情况下做判断即可。
自旋锁解决假唤醒问题
文章中介绍到线程也可能会发生假唤醒的状况,也就是在没有经过唤醒的情况下,自己醒过来继续执行,可以采用自旋锁的方式来解决这个问题,自旋这个概念之前似乎在哪里接触过,所以这个解决方案也并不难理解。在唤醒或假唤醒后,都通过while循环判断一下是否出现假唤醒的情况(判断条件为之前设置的记录等待与唤醒的bool变量记录),如果是假唤醒,那么顺理成章地让它继续回去休眠就好了。