这里提供一个java 代码例子并结合jps 和jstack来对java 基本线程api一探究竟。这里提供的代码(附在文后),与其说是例子,不如说是一个工具更合适些。你可以在eclipse下运行,也可以打成jar 包单独运行。它提供了一些命令,可以帮助我们理解java 的线程api,如果你在eclipse下运行,可以在控制台里使用这些命令,并通过jstack来观察线程发生了什么。
这些命令包括wait,建立一个wait 线程,使用object wait;notifyone ,建立一个notify one 线程,调用object notify 方法;notifyall 调用object notify all 方法;sleep,建立唯一sleep线程,调用sleep方法;join4sleeper,建立一个join线程,等待sleep 线程结束,调用join;wakeup,唤醒sleep 线程;lockall,获得锁,并持有锁,使用synchronized 块;unlock ,解锁;notifynaughty,notify all 但是不释放锁;stopnaughty,释放naughty 线程;waitwhile,有时间等待,调用 wait(time)。这里就是提供的基本命令。这里我们通过jstack来看一下java线程会是一个什么状态。
首先我们敲入wait,在通过jstack,会发现jstack显示线程状态为in object.wait()而Thread.state 为waiting(on object monitor)。
敲入waitwhile,使用jstack,会发现多一个线程,state为timed_waiting(on object monitor)。
再敲入notifyone,通过jstack 会发现少一个线程,控制台也会打出对应结束的等待线程。
再使用notifyone,会是另一停止等待。
可见每次notify 只会使一个等待线程结束等待。
我们再看一下notifyall,我们先执行多次wait,之后执行notifyall,发现控制台里打印出刚才的等待线程结束。可见object.notifyAll可以使“所有”的等待线程继续执行。这里的所有是指什么呢。为了理解这个,我们现要理解一个object monitor,在java里每个object实例都有一个monitor对象,用来管理线程同步,这个可以monitor看成是一个集合类实例,包含三部分,一个是获得这个monitor的 线程,另一个是包含所有尝试获得monitor,却被其他线程捷足先登的线程集合,和包含获得monitor 之后调用wait 方法的线程集。这里的所有就是指所有在调用wait 方法线程集内的线程。
是不是调用了notify,或notifyAll,in objet.wait()的线程就一定能继续执行呢?回答是不一定。
这里我们敲几次wait,之后执行,notifynaughty,发现wait 的线程并没有继续执行而结束,而是线程state 进入了Blocked 状态。这是怎么回事呢?这是因为,线程获得锁后执行wait,不是被notify了想继续执行就继续执行的,还要要从新获得monitor(monitor:“呔,舍弃了哥不是你说让哥回来就回来的”)。
notifynaughty,调用了notifyAll 但是并没有释放锁。(这家伙是挺讨厌的,让别人看着干着急)。这里Thread.state Blocked 状态是说线程未获得monitor 而被阻塞。
还有一个问题是,如果你发现,你每次使用notifyone,最先wait 的线程先执行,是不是notify 会使最先wait的线程先执行呢,notify 实现了公平等待?答案是这只是一个错觉,看一下notify的api doc 会发现不是这样的,不要误会。
我们再看一下lockall,执行lockall,再执行wait,jstack 发现多出来的线程并不是在in object.wait()和waiting 状态而是waiting for monitor entry 和 Blocked的,这进一步说明了,先要调用wait 方法,先要获得固有锁,如果没有获得,那么就要进入Blocked状态。直到锁被放弃。
之后我们再看一下sleep,会发现又多了一个线程,线程状态是wait on condition,state 是 Timed_Waiting(sleeping) 。
之后使用join4sleeper,会产生等待线程,这个线程的状态和调用wait 方法的线程状态一样。
之后是使用wakeup,会发现前面的两个线程已经执行完成后退出了。可以看出join 线程是在sleep 线程之后的。join 可以用来控制线程间的执行顺序。此外在sleep 方法是使用锁很tricky的事,因为sleep 不会释放锁,如果sleep 睡太沉,中断线程要先获得锁,那么王子是救不了睡美人的。
到现在,你果你仔细,会发现每次调用jstack主线程都是在同一条语句,这是因为主线程在等待用户输入调用了inputsteam 的read 方法而卡主了线程。可以发现线程是卡住的,但是状态是runnable 的可见对于oio(old io 对应nio)是和线程状态没啥关系的,且不可中断。(对应nio 不是这样的,而且可以中断对应线程,可以看intercept 的java api doc)
此外在java 线程API 中是不是所有的状态有waiting 的线程都可以中断?如果在java 1.6下,是的。但是由于新引入了显示锁,结果就不是了。5 显示锁的状态不现实貌似是。而显示锁 reentrylock 相对于synchronized的状态则是waiting(parking) 而jstack的线程状态和和sleep 一样是wait on condition。
此外固有锁无法获得锁,那么其他线程只能等,和尾生似的,你不来,哥就敢等个地老天荒。reentrylock 除了提供这种功能外,还有其它三种,trylock:拿不到,哥不伺候了,走你。trylock(time):不要这么急么,先等会再说,3秒钟不能再多了。lockInterruptibly:哥才不像你们呢,哥要一直等下去,你说什么,不来了,哥走了。(被中断,退出锁定)。
还有显示锁的性能要比固有锁好。java 6 接近一倍(java concurrency in practice)。要是看到这里,你可能要跳脚了,前面讲的那么大堆关于固有锁的没有啊?并不是的,因为显示锁和固有锁有相同的语义,你明白synchronized,wait,notify 你就能明白 reentrylock 和condition的用法。
后面示例程序,have fun
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.concurrent.atomic.AtomicInteger; public class Main { public static void main(String[] args) { ThreadTest test = new ThreadTest(); Thread sleeper = null; Thread naughty = null; BufferedReader reader = new BufferedReader(new InputStreamReader( System.in)); while (true) { try { String command = reader.readLine(); Command comm = null; try { comm = Enum.valueOf(Command.class, command.trim() .toLowerCase()); } catch (Exception e) { System.out.println("there is no this command"); continue; } switch (comm) { case exit: test.close(); test.beginNotifyAllThread(); if (sleeper != null) { sleeper.interrupt(); } if (naughty != null) { naughty.interrupt(); } System.out.println(comm.name()); return; case wait: test.beginWaitThread(); break; case notifyone: test.beginNotifyOneThread(); break; case notifyall: test.beginNotifyAllThread(); break; case notifynaughty: if (naughty != null) { naughty.interrupt(); } naughty = test.beginNotifAllNaughty(); break; case stopnaughty: if (naughty != null) { naughty.interrupt(); naughty = null; } else { System.out.println("there is no naughty boy"); } case sleep: if (sleeper == null) { sleeper = test.beginSleepThread(); } else { System.out.println("there is already a sleeper"); } break; case wakeup: if (sleeper != null) { sleeper.interrupt(); sleeper = null; } else { System.out.println("there is no sleeper"); } break; case lockall: test.beginLockAll(); break; case unlock: test.beginUnLock(); break; case waitwhile: test.beginWaiWhile(); break; case join4sleeper: if (sleeper != null) { test.beginJoinForSleeper(sleeper); } else { System.out.println("there is no sleeper"); } break; default: break; } } catch (IOException e) { System.out.println(e.getMessage()); } } } public enum Command { exit, wait, notifyone, notifyall, sleep, wakeup, notifynaughty, lockall, unlock, waitwhile, join4sleeper, stopnaughty; } public static class ThreadTest { Object lock = new Object(); volatile boolean wait = true; volatile boolean finish = false; volatile boolean locked = false; public static final int time = 5000; final Runnable notifyOneRun; final Runnable sleep; final Runnable waitRun; final Runnable notifyAllRun; final Runnable notifyAllNaughty; final Runnable lockAll; final Runnable unlock; final Runnable waitWhile; final AtomicInteger threadCount = new AtomicInteger(); public ThreadTest() { notifyAllNaughty = new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("notify all"); wait = false; lock.notifyAll(); try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { } } } }; waitWhile = new Runnable() { @Override public void run() { try { synchronized (lock) { wait = true; while (wait) { lock.wait(Integer.MAX_VALUE); } } System.out.println(new StringBuilder(Thread .currentThread().getName()).append("finish")); } catch (InterruptedException e) { System.out.println(new StringBuilder(Thread .currentThread().getName()) .append(" wait finish")); } } }; lockAll = new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("lock all"); locked = true; while (locked) { } System.out.println("unlocked now!"); ; } } }; unlock = new Runnable() { @Override public void run() { System.out.println("unlock"); locked = false; } }; sleep = new Runnable() { @Override public void run() { try { System.out.println("begin sleep"); while (!Thread.currentThread().isInterrupted()) { Thread.sleep(time); } System.out.println("should not happen"); } catch (InterruptedException e) { System.out.println(new StringBuilder(Thread .currentThread().getName()).append(" wake up")); } } }; waitRun = new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("begin wait"); try { wait = true; while (wait) { lock.wait(); } System.out.println(new StringBuilder(Thread .currentThread().getName()) .append("finish")); } catch (InterruptedException e) { System.out.println(new StringBuilder(Thread .currentThread().getName()) .append(" wait finish")); } } } }; notifyOneRun = new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("notify one"); wait = false; lock.notify(); } } }; notifyAllRun = new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("notify all"); wait = false; lock.notifyAll(); } } }; } public Thread beginWaitThread() { Thread t = new Thread(waitRun, "wait" + threadCount.addAndGet(1)); t.start(); return t; } public Thread beginNotifyOneThread() { Thread t = new Thread(notifyOneRun, "notifyOne" + threadCount.addAndGet(1)); t.start(); return t; } public Thread beginNotifyAllThread() { Thread t = new Thread(notifyAllRun, "notifyAll" + threadCount.addAndGet(1)); t.start(); return t; } public Thread beginSleepThread() { Thread t = new Thread(sleep, "sleep" + threadCount.addAndGet(1)); t.start(); return t; } public Thread beginLockAll() { Thread t = new Thread(lockAll, "lockAll" + threadCount.addAndGet(1)); t.start(); return t; } public Thread beginUnLock() { Thread t = new Thread(unlock, "unlock" + threadCount.addAndGet(1)); t.start(); return t; } public Thread beginWaiWhile() { Thread t = new Thread(waitWhile, "waitWhile" + threadCount.addAndGet(1)); t.start(); return t; } public Thread beginNotifAllNaughty() { Thread t = new Thread(notifyAllNaughty, "notifyAllNaughty" + threadCount.addAndGet(1)); t.start(); return t; } public Thread beginJoinForSleeper(final Thread sleeper) { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("begin join"); try { sleeper.join(); } catch (InterruptedException e) { } } }, "beginJoinForSleeper waiting" + sleeper.getName() + threadCount.addAndGet(1)); t.start(); return t; } public void close() { this.locked = false; } }
相关推荐
总的来说,这个实验将教会你如何在Java多线程环境中实现高效、稳定的文件上传和下载功能,这是一项对任何开发人员来说都非常实用的技能,尤其是在构建高性能的Web应用或后台服务时。通过实践和理解这些概念,你将...
录放音磁头的符号见图5(e),如果是双声道立体声的,就在符号上加一个“2”字。 扬声器和耳机是把电信号转换成声音的换能元件。耳机的符号见图5(g),文字符号是“BE”。扬声器的符号见图5(h),文字符号是“BL...
经典!实用!图中带有具体分析的方法,能够快速入门!
这个名为“12个例子教会你看电路图.zip”的压缩包包含了一个PDF文档,显然旨在通过一系列实例帮助初学者理解电路图的阅读和分析。下面,我们将深入探讨电路图的基本构成、常见元件符号以及如何解读电路图。 首先,...
《12个例子教会你看电路图》这份资料为我们提供了一套系统的学习方法,让我们能够通过具体的例子快速掌握阅读电路图的能力。下面,我们将深入探讨这份资料的核心内容,帮助你进一步理解电路图并掌握其阅读技巧。 ...
在电子工程领域,电路图是理解、设计和分析电子系统的关键工具。本教程"教你分析电路图(经典...每个例子都会涉及上述一个或多个知识点,通过实际操作和解构,将理论知识转化为实际技能,从而更好地理解和应用电路图。
首先创建一个名为`Hello.java`的文件,然后在其中输入源代码。使用`javac`编译器将源代码转换为字节码,最后通过`java`执行器运行程序。"一次编写,处处运行"的概念意味着Java程序可以在任何支持Java的平台上运行,...
Java编程语言是世界上最流行的开发语言之一,以其跨平台、面向对象和强大的功能而备受赞誉。...通过这些实例,你不仅可以学习到Java的基础知识,还能提升实际问题解决能力,为成为一个熟练的Java开发者打下坚实基础。
记住,学习编程并不止于理论,更重要的是动手实践,所以每学完一个知识点,都尝试自己编写相关的代码,这样你会更快地掌握并理解这些知识。祝你在Java游戏开发的道路上越走越远,享受编程带来的乐趣!
通过以上步骤,我们成功搭建了一个使用BlazeDS实现Java与Flex通信的基本环境。本教程不仅介绍了BlazeDS的基本概念,还详细指导了如何在本地环境中安装配置BlazeDS、编写Java服务端代码以及使用Flex Builder 3创建...
在这个例子中,我们可能会看到如何使用`AfxBeginThread`来启动一个工作线程,这个线程负责执行文件复制操作。 在多线程环境中,数据同步和通信是关键问题。MFC通过消息队列和消息循环机制来实现线程间的通信。工作...
在这个“3个类教会你Socket聊天”的教程中,我们将深入理解如何利用Java的Socket API构建一个简单的聊天应用。这个应用基于JDK 1.7,因此,所有的代码示例都应兼容这个版本。 1. **Socket类**: Java的`java.net....
免责声明:资料部分来源于合法的互联网渠道收集和整理,部分自己学习积累成果,供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者或出版方,资料版权归原作者或出版方所有,...
对Java语言的每个语法都提供了一个或多个例程讲解 大量使用流程图表示程序的执行过程,使用结构图表示程序的内部状态 每章最后都给出了典型的练习题,让读者及时练习,巩固提高,并提供了参考答案 目录 第1篇 ...
总的来说,"易语言多线程许可证基本例程源码"是一个实用的学习资源,它教会了我们如何在易语言环境中有效地管理和控制多线程,避免因并发访问引起的潜在问题。通过学习和实践这个例程,开发者可以提升其在多线程编程...
Elasticsearch Java 手册则专注于Elasticsearch的Java客户端,这是一个强大的全文搜索引擎和分析引擎。使用Elasticsearch Java API,开发者可以方便地与Elasticsearch集群交互,进行索引、查询、聚合数据以及管理...
对Java语言的每个语法都提供了一个或多个例程讲解 大量使用流程图表示程序的执行过程,使用结构图表示程序的内部状态 每章最后都给出了典型的练习题,让读者及时练习,巩固提高,并提供了参考答案 目录 第1篇 ...