`
zhouxy1123
  • 浏览: 6163 次
文章分类
社区版块
存档分类
最新评论

一个例子教会你java 基本线程api

 
阅读更多

 


 这里提供一个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;
		}
	}

 

 

  • 大小: 104 KB
  • 大小: 98 KB
  • 大小: 141 KB
  • 大小: 44.1 KB
  • 大小: 69.9 KB
分享到:
评论

相关推荐

    WHUT-java多线程实验-第三周-文件上传和下载.zip

    总的来说,这个实验将教会你如何在Java多线程环境中实现高效、稳定的文件上传和下载功能,这是一项对任何开发人员来说都非常实用的技能,尤其是在构建高性能的Web应用或后台服务时。通过实践和理解这些概念,你将...

    12个例子教会你看电路图

    录放音磁头的符号见图5(e),如果是双声道立体声的,就在符号上加一个“2”字。 扬声器和耳机是把电信号转换成声音的换能元件。耳机的符号见图5(g),文字符号是“BE”。扬声器的符号见图5(h),文字符号是“BL...

    11个例子教会你看电路图

    经典!实用!图中带有具体分析的方法,能够快速入门!

    12个例子教会你看电路图.zip

    这个名为“12个例子教会你看电路图.zip”的压缩包包含了一个PDF文档,显然旨在通过一系列实例帮助初学者理解电路图的阅读和分析。下面,我们将深入探讨电路图的基本构成、常见元件符号以及如何解读电路图。 首先,...

    12个例子教会你看电路图.pdf

    《12个例子教会你看电路图》这份资料为我们提供了一套系统的学习方法,让我们能够通过具体的例子快速掌握阅读电路图的能力。下面,我们将深入探讨这份资料的核心内容,帮助你进一步理解电路图并掌握其阅读技巧。 ...

    教你分析电路图(经典例子教会你)

    在电子工程领域,电路图是理解、设计和分析电子系统的关键工具。本教程"教你分析电路图(经典...每个例子都会涉及上述一个或多个知识点,通过实际操作和解构,将理论知识转化为实际技能,从而更好地理解和应用电路图。

    Java语言基础教程,教会大家编写基本的Java程序

    首先创建一个名为`Hello.java`的文件,然后在其中输入源代码。使用`javac`编译器将源代码转换为字节码,最后通过`java`执行器运行程序。"一次编写,处处运行"的概念意味着Java程序可以在任何支持Java的平台上运行,...

    java21个自学例子必备资料,附程序源代码

    Java编程语言是世界上最流行的开发语言之一,以其跨平台、面向对象和强大的功能而备受赞誉。...通过这些实例,你不仅可以学习到Java的基础知识,还能提升实际问题解决能力,为成为一个熟练的Java开发者打下坚实基础。

    21天学会Java游戏--在短的时间内教会你编写java游戏

    记住,学习编程并不止于理论,更重要的是动手实践,所以每学完一个知识点,都尝试自己编写相关的代码,这样你会更快地掌握并理解这些知识。祝你在Java游戏开发的道路上越走越远,享受编程带来的乐趣!

    用一个HelloWorld例子手把手教会你使用FLEX BlazeDS

    通过以上步骤,我们成功搭建了一个使用BlazeDS实现Java与Flex通信的基本环境。本教程不仅介绍了BlazeDS的基本概念,还详细指导了如何在本地环境中安装配置BlazeDS、编写Java服务端代码以及使用Flex Builder 3创建...

    MFC用户界面线程的创建示例

    在这个例子中,我们可能会看到如何使用`AfxBeginThread`来启动一个工作线程,这个线程负责执行文件复制操作。 在多线程环境中,数据同步和通信是关键问题。MFC通过消息队列和消息循环机制来实现线程间的通信。工作...

    3个类教会你Socket聊天

    在这个“3个类教会你Socket聊天”的教程中,我们将深入理解如何利用Java的Socket API构建一个简单的聊天应用。这个应用基于JDK 1.7,因此,所有的代码示例都应兼容这个版本。 1. **Socket类**: Java的`java.net....

    12个例子教会你看电路图.rar

    免责声明:资料部分来源于合法的互联网渠道收集和整理,部分自己学习积累成果,供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者或出版方,资料版权归原作者或出版方所有,...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    对Java语言的每个语法都提供了一个或多个例程讲解 大量使用流程图表示程序的执行过程,使用结构图表示程序的内部状态 每章最后都给出了典型的练习题,让读者及时练习,巩固提高,并提供了参考答案 目录 第1篇 ...

    易语言多线程许可证基本例程源码

    总的来说,"易语言多线程许可证基本例程源码"是一个实用的学习资源,它教会了我们如何在易语言环境中有效地管理和控制多线程,避免因并发访问引起的潜在问题。通过学习和实践这个例程,开发者可以提升其在多线程编程...

    Java API 版本的Vert.x Core 手册等三本书

    Elasticsearch Java 手册则专注于Elasticsearch的Java客户端,这是一个强大的全文搜索引擎和分析引擎。使用Elasticsearch Java API,开发者可以方便地与Elasticsearch集群交互,进行索引、查询、聚合数据以及管理...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    对Java语言的每个语法都提供了一个或多个例程讲解 大量使用流程图表示程序的执行过程,使用结构图表示程序的内部状态 每章最后都给出了典型的练习题,让读者及时练习,巩固提高,并提供了参考答案 目录 第1篇 ...

Global site tag (gtag.js) - Google Analytics