java线程同步问题——由腾讯笔试题引发的风波
刚刚wm问我了一道线程的问题,因为自己一直是coder界里的渣渣。所以就须要恶补一下。
2016年4月2号题目例如以下。
- import java.util.logging.Handler;
- /**
- * 完SyncTask的start方法,要求
- * 1,SyncTask的派生类的run方法抛到Handler所属的线程运行。
- * 2。SyncTask派生类的运行线程等待返回,除非等待的超时timeout
- * 3,假设timeout或出错。则返回默认值defultRet
- */
- public class wm {
- public abstract class SyncTask<R> {
- protected abstract R run();
- private R result;
- private byte[] lock = new byte[0];
- private boolean notified = false;
- private Runnable task = new Runnable() {
- @Override
- public void run() {
- R ret = SyncTask.this.run();
- synchronized (lock) {
- result = ret;
- lock.notify();
- notified = true;
- }
- }
- };
- /***
- * 将任务抛到其它线程,同步等待其返回结果
- * @param timeout 超过指定时间则直接返回ms
- * @param defaultRet 默认返回值。即超时后或出错的返回值
- * @param handler 运行线程handler
- * @return
- */
- public R start(final long timeout, final R defaultRet, Handler handler) {
- }
- }
- }
见,知乎 https://www.zhihu.com/question/43416744
1。基础知识
线程的等待与唤醒
- /**
- * Created by xk on 2016/4/2.
- */
- public class WaitTest {
- public static void main(String[] args) {
- ThreadA t1 = new ThreadA("t1");
- synchronized (t1) {
- try {
- //启动线程
- System.out.println(Thread.currentThread().getName() + " start t1");
- t1.start();
- System.out.println(Thread.currentThread().getName() + "wait()");
- t1.wait();
- System.out.println(Thread.currentThread().getName() + "continue");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- class ThreadA extends Thread {
- public ThreadA(String name) {
- super(name);
- }
- public void run() {
- synchronized (this) {
- System.out.println(Thread.currentThread().getName() + "call notify()");
- notify();
- }
- }
- }
输出
Object类中关于等待/唤醒的API具体信息例如以下:
notify() -- 唤醒在此对象监视器上等待的单个线程。
notifyAll() -- 唤醒在此对象监视器上等待的全部线程。
wait() -- 让当前线程处于“等待(堵塞)状态”,“直到其它线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout) -- 让当前线程处于“等待(堵塞)状态”,“直到其它线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”。当前线程被唤醒(进入“就绪状态”)。
wait(long timeout, int nanos) -- 让当前线程处于“等待(堵塞)状态”。“直到其它线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其它某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。
1. 在上述程序中,主线程是main。t1是main线程中启动的线程,而锁是t1对象的同步锁。
2. 主线程调用new 新建一个线程,通过synchronized(t1)来获取t1对象的同步锁。然后调用t1.start()来启动线程t1.
3. 主线程。运行wait释放t1的锁,进入等待(堵塞)状态。等待t1对象上的线程通过notify或者notifyAll将其唤醒。
4. 线程t1执行之后,通过synchronized(this)获取当前对象的锁,调用。notify唤醒当前对象上的等待的线程,即main。
5. 线程t1执行完成。释放当前对象的锁,紧接着,主线程获取t1对象的锁,接着执行。
补充,
1,t1.wait()是让“主线程main”等待。而不是t1.当前线程调用wait的时候,必须拥有该对象的同步锁,调用之后。释放该锁。直到等待的调用对象的同步锁的notify或者notifyAll方法,该线程就会获得该对象的同步锁,继续执行。
wait()的作用是让“当前线程”等待,而“当前线程”是指正在cpu上执行的线程!
这也意味着。尽管t1.wait()是通过“线程t1”调用的wait()方法,可是调用t1.wait()的地方是在“主线程main”中。而主线程必须是“当前线程”,也就是执行状态,才干够执行t1.wait()。
所以。此时的“当前线程”是“主线程main”!因此,t1.wait()是让“主线程”等待。而不是“线程t1”!
wait(long timeout)会让当前线程处于“等待(堵塞)状态”。“直到其它线程调用此对象的 notify() 方法或 notifyAll() 方法。或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout)会让当前线程处于“等待(堵塞)状态”,“直到其它线程调用此对象的 notify() 方法或 notifyAll() 方法。或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
以下的演示样例就是演示wait(long timeout)在超时情况下,线程被唤醒的情况。
// WaitTimeoutTest.java的源代码
class ThreadA extends Thread{
public ThreadA(String name) {
super(name);
}
public void run() {
System.out.println(Thread.currentThread().getName() + " run ");
// 死循环。不断执行。
while(true)
}
}
public class WaitTimeoutTest {
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
synchronized(t1) {
try {
// 启动“线程t1”
System.out.println(Thread.currentThread().getName() + " start t1");
t1.start();
// 主线程等待t1通过notify()唤醒 或 notifyAll()唤醒。或超过3000ms延时;然后才被唤醒。
System.out.println(Thread.currentThread().getName() + " call wait ");
t1.wait(3000);
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
执行结果:
main start t1
main call wait
t1 run // 大约3秒之后...输出“main continue”
main continue
结果说明:
例如以下图。说明了“主线程”和“线程t1”的流程。
(01) 注意,图中"主线程" 代表WaitTimeoutTest主线程(即,线程main)。"线程t1" 代表WaitTest中启动的线程t1。
而“锁” 代表“t1这个对象的同步锁”。
(02) 主线程main运行t1.start()启动“线程t1”。
(03) 主线程main执行t1.wait(3000),此时,主线程进入“堵塞状态”。
须要“用于t1对象锁的线程通过notify() 或者 notifyAll()将其唤醒” 或者 “超时3000ms之后”,主线程main才进入到“就绪状态”。然后才干够执行。
(04) “线程t1”执行之后,进入了死循环,一直不断的执行。
(05) 超时3000ms之后,主线程main会进入到“就绪状态”,然后接着进入“执行状态”。
4. wait() 和 notifyAll()
通过前面的演示样例,我们知道 notify() 能够唤醒在此对象监视器上等待的单个线程。
以下,我们通过演示样例演示notifyAll()的使用方法;它的作用是唤醒在此对象监视器上等待的全部线程。
public class NotifyAllTest {
private static Object obj = new Object();
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
ThreadA t2 = new ThreadA("t2");
ThreadA t3 = new ThreadA("t3");
t1.start();
t2.start();
t3.start();
try {
System.out.println(Thread.currentThread().getName()+" sleep(3000)");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(obj) {
// 主线程等待唤醒。
System.out.println(Thread.currentThread().getName()+" notifyAll()");
obj.notifyAll();
}
}
static class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run() {
synchronized (obj) {
try {
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " wait");
// 唤醒当前的wait线程
obj.wait();
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
执行结果:
t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue
结果说明:
參考以下的流程图。
(01) 主线程中新建而且启动了3个线程"t1", "t2"和"t3"。
(02) 主线程通过sleep(3000)休眠3秒。在主线程休眠3秒的过程中。我们如果"t1", "t2"和"t3"这3个线程都执行了。以"t1"为例,当它执行的时候,它会执行obj.wait()等待其他线程通过notify()或额nofityAll()来唤醒它;同样的道理。"t2"和"t3"也会等待其他线程通过nofity()或nofityAll()来唤醒它们。
(03) 主线程休眠3秒之后,接着执行。
执行 obj.notifyAll() 唤醒obj上的等待线程。即唤醒"t1", "t2"和"t3"这3个线程。 紧接着,主线程的synchronized(obj)执行完成之后,主线程释放“obj锁”。
这样,"t1", "t2"和"t3"就能够获取“obj锁”而继续执行了!
5. 为什么notify(), wait()等函数定义在Object中,而不是Thread中
Object中的wait(), notify()等函数。和synchronized一样,会对“对象的同步锁”进行操作。
wait()会使“当前线程”等待,由于线程进入等待状态。所以线程应该释放它锁持有的“同步锁”。否则其他线程获取不到该“同步锁”而无法执行!
OK。线程调用wait()之后,会释放它锁持有的“同步锁”。并且。根据前面的介绍,我们知道:等待线程能够被notify()或notifyAll()唤醒。
如今,请思考一个问题:notify()是根据什么唤醒等待线程的?或者说。wait()等待线程和notify()之间是通过什么关联起来的?答案是:根据“对象的同步锁”。
负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它仅仅有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),而且调用notify()或notifyAll()方法之后,才干唤醒等待线程。尽管,等待线程被唤醒;可是。它不能立马执行。由于唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才干获取到“对象的同步锁”进而继续执行。
总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,而且每一个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类。而不是Thread类中的原因。
java线程同步问题——由腾讯笔试题引发的风波的更多相关文章
- java 线程同步 原理 sleep和wait区别
java线程同步的原理java会为每个Object对象分配一个monitor, 当某个对象(实例)的同步方法(synchronized methods)被多个线程调用时,该对象的monitor将负责处 ...
- Java线程同步_1
Java线程同步_1 synchronized 该同步机制的的核心是同步监视器,任何对象都可以作为同步监视器,代码执行结束,或者程序调用了同步监视器的wait方法会导致释放同步监视器 synchron ...
- Java线程同步之一--AQS
Java线程同步之一--AQS 线程同步是指两个并发执行的线程在同一时间不同时执行某一部分的程序.同步问题在生活中也很常见,就比如在麦当劳点餐,假设只有一个服务员能够提供点餐服务.每个服务员在同一时刻 ...
- java线程 同步临界区:thinking in java4 21.3.5
java线程 同步临界区:thinking in java4 21.3.5 thinking in java 4免费下载:http://download.csdn.net/detail/liangru ...
- JAVA - 线程同步和线程调度的相关方法
JAVA - 线程同步和线程调度的相关方法 wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁:wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等 ...
- Java线程同步的四种方式详解(建议收藏)
Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen 目录 什么是线程同步 线程同步的几种方式 1.使用sync ...
- 算法题14 小Q歌单,牛客网,腾讯笔试题
算法题14 小Q歌单,牛客网,腾讯笔试题 题目: 小Q有X首长度为A的不同的歌和Y首长度为B的不同的歌,现在小Q想用这些歌组成一个总长度正好为K的歌单,每首歌最多只能在歌单中出现一次,在不考虑歌单内歌 ...
- 算法题16 贪吃的小Q 牛客网 腾讯笔试题
算法题16 贪吃的小Q 牛客网 腾讯笔试题 题目: 链接:https://www.nowcoder.com/questionTerminal/d732267e73ce4918b61d9e3d0ddd9 ...
- Java线程同步和线程通信
一.线程同步 当多个线程访问同一个数据时,非常容易出现线程安全问题.这时候就需要用线程同步. 不可变类总是线程安全的,因为它的对象状态是不可改变的,但可变类对象需要额外的方法来保证线程安全. 1.同步 ...
随机推荐
- linux——(4)磁盘与文件系统管理
概念一:linux-ext2文件系统 ext2在分区的时候会分成多个组块(block group)和一个启动扇区(boot sector),每一个组块内又有superblock.File system ...
- hibernate 基于主键的单向一对一关联映射
1.设计表结构 表结构对于基于外键的关联关系来说就少了外键的关联列,并且两张表共用同一个ID,表示一对一. 2.创建Person对象 3.创建IdCard对象 4.写hbm.xml文件 5.生成数据库 ...
- WebLogic Server
前几天,看了几集J2ee , 给我的感觉就是,看不懂!! 一点也不懂! 那怎么办呢? 听老师的,不管懂不懂,先看看再说.接下来,就开始了J2ee "艰苦"的历程.在J2ee中,经常 ...
- stl upper_bound()
http://blog.csdn.net/niushuai666/article/details/6734650 upper_bound( a , b , k )返回有序升序序列[a,b)中能放下 ...
- 内网渗透利器--reDuh(webshell跳板)
作者:sai52[B.H.S.T] 国外大牛的作品,偶顺手写了个使用说明.E文好的看原文 http://www.sensepost.com/research/reDuh/ 这个工具可以把内网服务器的 ...
- poj 2912 并查集(食物链加强版)
题目:给出n个人玩剪刀石头布的游戏,其中有一个人是裁判,剩下的人分为3组,每一组的人只出某一种手型,裁判可以任意出.问是否能判断出哪个人是裁判 链接:点我 分分钟看吐血,先把食物链看懂吧 枚举裁判,然 ...
- Request Response 跳转页面的理解
1.response 跳转页面 private void writeContent(String content) { HttpServletResponse response = ((Servlet ...
- Read UNIQUE ID and flash size method for stm32
/* 读取stm32的unique id 与 flash size */ /* func: unsigned int Read_UniqueID_Byte(unsigned char offset) ...
- Windows Embedded Compact 7网络编程概述(下)
11.1.1 Select I/O模型 在Windows CE中,Select模型是唯一被支持的I/O模型.Select I/O模型就是利用select函数对I/O进行管理. 函数select的功能在 ...
- wpf简单的绘图板
xaml: <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft ...