- 浏览: 6303295 次
- 性别:
- 来自: 一片神奇的土地
文章分类
- 全部博客 (745)
- JQuery (25)
- JS (33)
- 数据库 (59)
- Java基础 (56)
- JSON (8)
- XML (8)
- ireport (7)
- 设计模式 (10)
- 心情 (14)
- freemarker (1)
- 问题 (15)
- powerdesigner (2)
- CSS (15)
- DWR (4)
- tomcat (16)
- Hibernate (12)
- Oracle (7)
- Struts (7)
- Spring (34)
- JSP (23)
- 需学习 (64)
- 工具类库 (63)
- Maven (14)
- 笔试题 (34)
- 源码学习 (31)
- 多线程 (39)
- Android (32)
- 缓存 (20)
- SpringMVC (14)
- jQueryEasyUi (12)
- webservice-RPC (13)
- ant (1)
- ASP.NET (10)
- 正则表达式 (3)
- Linux (15)
- JBoss (1)
- EJB (3)
- UML (2)
- JMS (3)
- Flex (8)
- JSTL (2)
- 批处理 (5)
- JVM (16)
- 【工具】 (16)
- 数据结构 (29)
- HTTP/TCP/Socket (18)
- 微信 (1)
- tomcat源码学习 (15)
- Python (30)
- 主机 (2)
- 设计与架构 (19)
- thrift-RPC (2)
- nginx (6)
- 微信小程序 (0)
- 分布式+集群 (12)
- IO (1)
- 消息队列 (4)
- 存储过程 (8)
- redis (9)
- zookeeper (5)
- 海量数据 (5)
最新评论
-
360pluse:
技术更新,战术升级!Python爬虫案例实战从零开始一站通网盘 ...
Python爬虫实战:Scrapy豆瓣电影爬取 -
18335864773:
推荐用 pageoffice 组件生成 word 文件。
JAVA生成WORD工具类 -
jjhe369:
LISTD_ONE 写道起始地址为163.135.0.1 结束 ...
IP地址与CIDR -
baojunhu99:
private final int POOL_SIZE = 5 ...
使用CompletionService获取多线程返回值 -
LovingBaby:
胡说,javascript 运行时是单线程的,event lo ...
Ajax请求是否可以实现同步
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
t.join(); //使调用线程 t 在此之前执行完毕。
t.join(1000); //等待 t 线程,等待时间是1000毫秒
先上一段JDK中代码:
/** * Waits at most <code>millis</code> milliseconds for this thread to * die. A timeout of <code>0</code> means to wait forever. */ //此处A timeout of 0 means to wait forever 字面意思是永远等待,其实是等到t结束后。 public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
从代码上看,如果线程被生成了,但还未被起动,调用它的 join() 方法是没有作用的,将直接继续向下执行
Join方法实现是通过wait(小提示:Object 提供的方法)。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁
Example1:
public class JoinTest implements Runnable{ public static int a = 0; public void run() { for (int k = 0; k < 5; k++) { a = a + 1; } } public static void main(String[] args) throws Exception { Runnable r = new JoinTest(); Thread t = new Thread(r); t.start(); System.out.println(a); } }
请 问程序的输出结果是5吗?答案是:有可能。其实你很难遇到输出5的时候,通常情况下都不是5。当然这也和机器有严重的关系。为什么呢?我的解释是当主线程 main方法执行System.out.println(a);这条语句时,线程还没有真正开始运行,或许正在为它分配资源准备运行。因为为线程分配资源需要时间,而main方法执行完t.start()方法后继续往下执行System.out.println(a);,这个时候得到的结果是a还没有被 改变的值0 。怎样才能让输出结果为5!其实很简单,join() 方法提供了这种功能。join() 方法,它能够使调用该方法的线程在此之前执行完毕。
public static void main(String[] args) throws Exception { Runnable r = new JoinTest(); Thread t = new Thread(r); t.start(); t.join(); //加入join() System.out.println(a); }
这个时候,程序输入结果始终为5。
为
了证明如果不使用t.join()方法,主线程main方法的System.out.println(a);语句将抢先执行,我们可以在main方法中加入一个循环,这个循环用来延长main方法执行的时间,循环次数将严重取决于机器性能。如果循环次数得当,我们也可以看到a的输出结果是5。
public static void main(String[] args) throws Exception { Runnable r = new JoinTest(); Thread t = new Thread(r); t.start(); //t.join(); //加入join() /* 注意循环体内一定要有实际执行语句,否则编译器或JVM可能优化掉你的这段代码,视这段代 码为无效。 */ for (int i=0; i<300; i++) { System.out.print(i); } System.out.println(); System.out.println(a); }
经自己测试,最后a一直是5.
本例参考:http://agio.iteye.com/blog/210600
Example2:join(n)
class RunnableImpl implements Runnable { public void run() { try { System.out.println("Begin sleep"); Thread.sleep(1000); System.out.println("End sleep"); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class JoinTest{ public static void main(String[] args) { Thread t = new Thread(new RunnableImpl()); t.start(); try { t.join(1000); System.out.println("joinFinish"); } catch (InterruptedException e) { e.printStackTrace(); } } }
结果是:
Begin sleep
End sleep
joinFinish
明白了吧,当main线程调用t.join时,main线程等待t线程,等待时间是1000,如果t线程Sleep 2000呢
class RunnableImpl implements Runnable { public void run() { try { System.out.println("Begin sleep"); Thread.sleep(2000); //原来为1000 System.out.println("End sleep"); } catch (InterruptedException e) { e.printStackTrace(); } } }
结果是:
Begin sleep
joinFinish
End sleep
也就是说main线程只等1000毫秒,不管T什么时候结束.
参考:http://blog.csdn.net/FG2006/archive/2011/05/04/6393768.aspx
Example3:
class CustomThread1 extends Thread { public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " start."); try { for (int i = 0; i < 5; i++) { System.out.println(threadName + " loop at " + i); Thread.sleep(1000); } System.out.println(threadName + " end."); } catch (Exception e) { System.out.println("Exception from " + threadName + ".run"); } } } class CustomThread extends Thread { CustomThread1 t1; public CustomThread(CustomThread1 t1) { this.t1 = t1; } public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " start."); try { t1.join(); System.out.println(threadName + " end."); } catch (Exception e) { System.out.println("Exception from " + threadName + ".run"); } } } public class JoinTestDemo { public static void main(String[] args) { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " start."); CustomThread1 t1 = new CustomThread1(); CustomThread t = new CustomThread(t1); try { t1.start(); Thread.sleep(2000); t.start(); t.join(); //在代碼2里,將此處注釋掉 } catch (Exception e) { System.out.println("Exception from main"); } System.out.println(threadName + " end!"); } }
结果:
main start. //main方法所在的线程起动,但没有马上结束,因为调用t.join();,所以要等到t结束了,此线程才能向下执行。
[CustomThread1] Thread start. //线程CustomThread1起动
[CustomThread1] Thread loop at 0 //线程CustomThread1执行
[CustomThread1] Thread loop at 1 //线程CustomThread1执行
[CustomThread] Thread start. //线程CustomThread起动,但没有马上结束,因为调用t1.join();,所以要等到t1结束了,此线程才能向下执行。
[CustomThread1] Thread loop at 2 //线程CustomThread1继续执行
[CustomThread1] Thread loop at 3 //线程CustomThread1继续执行
[CustomThread1] Thread loop at 4 //线程CustomThread1继续执行
[CustomThread1] Thread end. //线程CustomThread1结束了
[CustomThread] Thread end. // 线程CustomThread在t1.join();阻塞处起动,向下继续执行的结果
main end! //线程CustomThread结束,此线程在t.join();阻塞处起动,向下继续执行的结果。
将上例中的join注释掉:
public class JoinTestDemo { public static void main(String[] args) { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " start."); CustomThread1 t1 = new CustomThread1(); CustomThread t = new CustomThread(t1); try { t1.start(); Thread.sleep(2000); t.start(); //t.join(); } catch (Exception e) { System.out.println("Exception from main"); } System.out.println(threadName + " end!"); } }
结果:
main start. // main方法所在的线程起动,但没有马上结束,这里并不是因为join方法,而是因为Thread.sleep(2000);
[CustomThread1] Thread start. //线程CustomThread1起动
[CustomThread1] Thread loop at 0 //线程CustomThread1执行
[CustomThread1] Thread loop at 1 //线程CustomThread1执行
main end! // Thread.sleep(2000);结束,虽然在线程CustomThread执行了t1.join();,但这并不会影响到其他线程(这里main方法所在的线程)。
[CustomThread] Thread start. //线程CustomThread起动,但没有马上结束,因为调用t1.join();,所以要等到t1结束了,此线程才能向下执行。
[CustomThread1] Thread loop at 2 //线程CustomThread1继续执行
[CustomThread1] Thread loop at 3 //线程CustomThread1继续执行
[CustomThread1] Thread loop at 4 //线程CustomThread1继续执行
[CustomThread1] Thread end. //线程CustomThread1结束了
[CustomThread] Thread end. // 线程CustomThread在t1.join();阻塞处起动,向下继续执行的结果
本例参考:http://blog.csdn.net/bzwm/archive/2009/02/12/3881392.aspx
Example4 :
main 线程调用t.join时,必须能够拿到线程t对象的锁,如果拿不到它是无法wait的 ,刚开的例子t.join(1000)不是说明了main线程等待1 秒,如果在它等待之前,其他线程获取了t对象的锁,它等待时间可不就是1毫秒了 。
class RunnableImpl implements Runnable { public void run() { try { System.out.println("Begin sleep"); Thread.sleep(2000); System.out.println("End sleep"); } catch (InterruptedException e) { e.printStackTrace(); } } }
class ThreadTest extends Thread { Thread thread; public ThreadTest(Thread thread) { this.thread = thread; } @Override public void run() { synchronized (thread) { System.out.println("getObjectLock"); try { Thread.sleep(9000); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("ReleaseObjectLock"); } } }
public class JoinTest { public static void main(String[] args) { Thread t = new Thread(new RunnableImpl()); new ThreadTest(t).start(); t.start(); try { t.join(); System.out.println("joinFinish"); } catch (InterruptedException e) { e.printStackTrace(); } } }
结果:
getObjectLock
Begin sleep
End sleep
ReleaseObjectLock
joinFinish
在main方法中 通过new ThreadTest(t).start()实例化 ThreadTest 线程对象, 它 通过 synchronized (thread) ,获取线程对象t的锁,并Sleep(9000)后释放,这就意味着,即使main方法t.join(1000)等待一秒钟,它必须等待ThreadTest 线程释放t锁后才能进入wait方法中,它实际等待时间是9000+1000ms。
例子参考Example2来源.
评论
实际等待时间还是约9ms,“t.join(1000);”和“Sleep(9000)”是同时的,所以总时间上不会是和的结果。
再次验证:执行9000ms还是9000+1000ms,取决于join执行到还是ThreadTest的run先执行到,如果join先执行到,为9000ms,如果ThreadTest先执行到则是9000+1000ms
9000ms和9000ms+1000ms的差异在于:
如果join先获取到锁,join中记录启动时间now,进入wait,ThreadTest启动获取到锁,ThreadTest休眠9000ms,唤醒join,若join的线程结束了,则join退出,总共时间为9000ms,若线程没结束,则唤醒时间-启动时间也大于1000ms,也退出;所以这种情况下都是9000ms;
如果ThreadTest先获取到锁,休眠9000ms,回到主线程执行join,如join的线程结束,则join也结束,总共时间为9000ms;若join线程未结束(将t的sleep 时间改为9500ms或者20000ms),两种情况,若t的执行时间 > 9000ms+1000ms,则总时间为9000ms+1000ms,否则为t的执行时间;
thread.join的时间跟t的执行时间有关,是因为源码中的isAlive导致的
实际等待时间还是约9ms,“t.join(1000);”和“Sleep(9000)”是同时的,所以总时间上不会是和的结果。
再次验证:执行9000ms还是9000+1000ms,取决于join执行到还是ThreadTest的run先执行到,如果join先执行到,为9000ms,如果ThreadTest先执行到则是9000+1000ms
实际等待时间还是约9ms,“t.join(1000);”和“Sleep(9000)”是同时的,所以总时间上不会是和的结果。
t.join(1000)是让main线程等待1000ms或t死掉后执行。
这两种理解的关键在于:
“进入同步代码块的线程可以执行Thread.sleep()或者执行Thread.yield()方法,此时它并不释放对象锁,只是把运行的机会让给其他的线程”
‘锁对象’和‘其他线程’是同一个对象时,这个线程是否得到了执行?
必须获得锁才能执行;
就楼主这个,虽然ThreadTest先start了,但是应该是先执行到t.join,t中记录执行时间,然后释放锁,等待唤醒;然后ThreadTest得到锁,sleep 9000ms,然后唤醒
【wait不是释放当前线程的锁,并加入阻塞队列,等待notify或者notifyall将其唤醒】
是笔误了吗?
wait确实是释放,notify或者notifyall是唤醒
实际等待时间还是约9ms,“t.join(1000);”和“Sleep(9000)”是同时的,所以总时间上不会是和的结果。
亲测试,确实是9s左右,不知楼主不看评论吗?有错也不改,误人子弟!
【wait不是释放当前线程的锁,并加入阻塞队列,等待notify或者notifyall将其唤醒】
是笔误了吗?
神经病啊你!!!!!!
实际等待时间还是约9ms,“t.join(1000);”和“Sleep(9000)”是同时的,所以总时间上不会是和的结果。
t.join(1000)是让main线程等待1000ms或t死掉后执行。
这两种理解的关键在于:
“进入同步代码块的线程可以执行Thread.sleep()或者执行Thread.yield()方法,此时它并不释放对象锁,只是把运行的机会让给其他的线程”
‘锁对象’和‘其他线程’是同一个对象时,这个线程是否得到了执行?
实际等待时间还是约9ms,“t.join(1000);”和“Sleep(9000)”是同时的,所以总时间上不会是和的结果。
t.join(1000)是让main线程等待1000ms或t死掉后执行。
实际等待时间还是约9ms,“t.join(1000);”和“Sleep(9000)”是同时的,所以总时间上不会是和的结果。
不错
发表评论
-
AQS
2019-03-21 15:08 2025大白话聊聊Java并发面试问题之谈谈你对AQS的理解? ... -
JAVA内存模型-(程锁V传+启断终结)
2018-04-25 11:29 972Java并发编程:volatile关键字解析-(重要,分析 ... -
ArrayBlockingQueue
2018-04-18 15:54 1255ArrayBlockingQueue p ... -
【多线程总结】
2017-12-12 15:53 636线程需要注意的地方: 1、 public final s ... -
服务器集群对Synchronized有没有什么影响
2015-11-11 15:39 5385有个功能大致如下,在一堆没用过的数据中取一条数据,并将其标 ... -
获取id 的一种策略
2015-07-07 17:48 1923从数据库中批量(step个)拿出Id,然后使用,待消耗完后 ... -
模拟的线程池
2014-09-25 09:51 1741自定义数据库连接池 例子: public cla ... -
Thread例子
2014-09-24 17:53 5823看看对Thread到底懂多少,嘿嘿 Exampl ... -
FairSync与NonfairSync
2014-09-24 16:50 6619state 为0,表示锁未被获取 不为0,表示已被获取 ... -
线程安全并且无阻塞的Atomic类
2014-09-24 09:03 6673原子操作AtomicInteger public c ... -
BlockingQueue
2014-09-19 09:51 1296循环队列与优先级队 ... -
Lock的await/singal 和 Object的wait/notify 的区别
2014-09-18 11:23 2216在使用Lock之前,我们都使用Object 的wait和no ... -
多线程类总结
2014-09-16 14:12 1406Executor execute(R ... -
Condition
2014-09-15 13:52 1512Ojbect: wait\noti ... -
Wait-Notify机制
2014-09-15 09:50 1934Wait-Notify机制可以说是实现阻塞操作较为高效的一 ... -
用guava实现简单的事件驱动
2014-07-25 10:28 7968Guava的EventBus可以简化生产/消费模型。Eve ... -
这个java程序,为什么一个notify唤醒了3个wait
2012-10-23 17:01 13711、 public class Demo6 { publ ... -
servlet是否是线程安全的
2012-08-06 20:48 3360Servlet/JSP技术 ... -
LinkedBlockingQueue + 单向链表基本结构
2012-06-15 17:28 2357LinkedBlockingQueue是一个单向链表结构的队列 ... -
利用ReentrantReadWriteLock实现缓存系统
2012-06-05 14:17 2788首先解释下缓存系统: 在程序运行 ...
相关推荐
Java线程中wait、await、sleep、yield、join用法汇总,文章里面总结了这些关键字的用法,并且里面带有源码帮助分析用法,此一文就可以理解这些关键字用法,推荐拥有
虽然我个人认为我们当中很少有人能真正获得机会开发复杂的多线程应用(在过去的七年中,我得到了一个机会),但是理解多线程对增加你的信心很有用。之前,我讨论了一个wait()和sleep()方法区别的问题,这一次,我将会...
本章内容 掌握同步代码块的使用 掌握同步方法的使用 理解线程死锁 掌握 ThreadLocal 类的使用 使用多线程模拟猴子采花 使用同步方法模拟购票 使用多线程模拟购物订单生成 使用 ThreadLocal 类模拟银行取款 Java高级...
文章目录神标题引入线程和进程多线程的优势线程创建方式继承Thread类来创建和启动实现Runnable接口重写run方法创建线程类使用 Callable 和 Future 创建线程三种创建线程方式做出对比线程生命周期线程控制join线程...
同一个进程中的多个线程之间可以并发执行 在Java中,每次程序运行至少启动2个线程:一个是main线程,一个是垃圾收gc集线程。每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实际上就是在操作...
三、:locked_with_key:Java多线程与并发框架:unlocked: Java多线程与并发框 (第 13 篇) 深入理解:Fork/Join框架 Java多线程与并发框 (第 14 篇) 深入理解:原子操作 Java多线程与并发框 (第 15 篇) 深入理解...
JAVA中如何实现多线程(重点!!) 168 通过继承Thread类实现多线程 168 通过Runnable接口实现多线程 169 线程状态和sleep/yield/join/stop/destroy方法 170 新生状态 170 就绪状态 170 运行状态 170 死亡状态 170 ...
【多线程】Java四种线程池的创建方法 83 【多线程】线程池原理和运行机制 83 【多线程】线程池对任务的处理 85 【多线程】线程池的状态 86 线程池的状态说明 86 各个状态之间的转换 86 【多线程】什么是线程池?如果...
第1章 Java概述 1 1.1 Java语言的发展简史 2 1.2 Java的竞争对手及各自优势 4 1.2.1 C#简介和优势 4 1.2.2 Ruby简介和优势 4 1.2.3 Python的简介和优势 5 1.3 Java程序运行机制 5 1.3.1 高级语言的运行机制 6...
了解多线程所带来的安全风险.mp4 从线程的优先级看饥饿问题.mp4 从Java字节码的角度看线程安全性问题.mp4 synchronized保证线程安全的原理(理论层面).mp4 synchronized保证线程安全的原理(jvm层面).mp4 单例问题...
第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个阶段并推荐学习并发的资料 ...
1.1 理解Java 2 1.2 搭建Java所需环境 3 1.2.1 下载JDK 3 1.2.2 安装JDK 4 1.2.3 配置环境 5 1.2.4 测试JDK配置是否成功 7 实例1 开发第一个Java程序 7 第2章 Java基础类型与运算符...
实例232 多线程同步方法的实例 436 实例233 ATM存取一体机(线程同步互斥) 437 实例234 我的钱哪里去了 440 实例235 门锁打不开了(死锁) 444 实例236 门锁终于被打开了(解决死锁) 446 实例237 一个死锁的例子 ...
1.1 理解Java 2 1.2 搭建Java所需环境 3 1.2.1 下载JDK 3 1.2.2 安装JDK 4 1.2.3 配置环境 5 1.2.4 测试JDK配置是否成功 7 实例1 开发第一个Java程序 7 第2章 Java基础类型与运算符(教学...
第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个阶段并推荐学习并发的资料 ...
实例232 多线程同步方法的实例 436 实例233 ATM存取一体机(线程同步互斥) 437 实例234 我的钱哪里去了 440 实例235 门锁打不开了(死锁) 444 实例236 门锁终于被打开了(解决死锁) 446 实例237 一个死锁的...
理解Java中对象基础Object类 基本数据类型,核心点整理 特殊的String类,以及相关扩展API 日期与时间API详解 流程控制语句,和算法应用 函数式编程概念和应用 集合容器 基于分析列表集合源码体系 基于分析地图集合...
│ 高并发编程第一阶段26讲、多线程下的生产者消费者模型,以及详细介绍notifyAll方法.mp4 │ 高并发编程第一阶段27讲、wait和sleep的本质区别是什么,深入分析(面试常见问题).mp4 │ 高并发编程第一阶段28讲、...
实例232 多线程同步方法的实例 436 实例233 ATM存取一体机(线程同步互斥) 437 实例234 我的钱哪里去了 440 实例235 门锁打不开了(死锁) 444 实例236 门锁终于被打开了(解决死锁) 446 实例237 一个死锁的例子 ...