Java中的main线程是不是最后一个退出的线程

之所以写这篇文章,是因为上次被人问到这么一个问题:“在main函数里启动一个定时器,是不是main函数执行完整个程序就退出了,包括那个定时器”。多说无益,直接写个程序测试一下就知道了。


public class MainThreadTest {

	public static void main(String[] args) {
		new Timer().schedule(new TimerTask(){

			@Override
			public void run() {
				System.out.println("Timer thread is running...");
			}

		}, 500, 500);

		System.out.println("Main thread ends!");
	}
}

然后你就会发现执行结果是这样的:

Main thread ends!
Timer thread is running...
Timer thread is running...
Timer thread is running...

这至少说明main函数执行完毕之后,定时器依旧在运行,那么main线程是否退出了呢?我想不少同学可能会和我之前一样脑补这样的逻辑:“timer线程是main线程创建的,main线程会等到自己创建的所有线程都退出后才会退出”,现实当中我们有时真的会被一些自己妄自推断的结论给弄得雨里雾里,最好的做法是在自己产生一个想法之后想办法去证明它或者证否它。回到这个例子,实际上,当前有哪些线程在运行,我们是可以看到的。怎么看?用java自带的工具jstack就能看到,jstack的使用方法就不多说了,请自行搜索。通过jstack我们可以把jvm当前的线程状态dump下来,在dump结果里你会看到很多陌生的线程,这都没关系,你只需要关注下面的两个线程就好。

"Timer-0" prio=6 tid=0x01b8f800 nid=0x1330 in Object.wait() [0x0c10f000..0x0c10f
be8]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x03bcdd18> (a java.util.TaskQueue)
at java.util.TimerThread.mainLoop(Timer.java:509)
- locked <0x03bcdd18> (a java.util.TaskQueue)
at java.util.TimerThread.run(Timer.java:462)

"main" prio=6 tid=0x01989400 nid=0x4b8 at breakpoint[0x003bf000..0x003bfe24]
java.lang.Thread.State: RUNNABLE
at me.gemoji.www.test.MainThreadTest.main(MainThreadTest.java:18)

上面的线程状态是我在main函数中加断点后运行看到的结果,你会看到这时候main线程肯定是还存在的,而且可以明确地看到它处于”at breakpoint”的状态。那么要是我让main函数继续执行下去呢?你会发现main线程不见了,而定时器的线程依旧在运行,这就充分证明main线程在main函数执行完之后就退出了,并不是最后一个退出的线程。

上面是通过实践得出的结论,下面补充一下理论。stackoverflow上有个类似的问题:When does the main thread stop in Java?,把里面的答案总结一下,基本上就这么三句话:

  1. JVM会在所有的非守护线程(用户线程)执行完毕后退出;
  2. main线程是用户线程;
  3. 仅有main线程一个用户线程执行完毕,不能决定JVM是否退出,也即是说main线程并不一定是最后一个退出的线程。

关于守护线程的相关知识,大家可以自行搜索下,另外,如果你对jstack打印的结果中的其他线程感兴趣,可以参考这篇文章

[备忘]SAE绑定独立域名后的一些后续扫尾工作

写在前面

上周终于给blog换上了独立域名,但还是有些小问题,今天正好有时间就在网上搜了搜解决方法,刚折腾完,趁着新鲜热乎劲儿赶紧做个备忘。

问题一:使用独立域名访问后,点击其他页面访问的仍是sinaapp的二级域名

这是因为没有修改wordpress的url配置的缘故,只需要登录wordpress后台,在“设置-常规”里将“WordPress 地址(URL)”和“站点地址(URL)”两项修改为新域名即可。如果仅修改了“WordPress 地址(URL)”的话,可能会导致wordpress后台无法登录,这个时候可以尝试这篇文章里提到的几种方法解决。

问题二:如何让原有的使用sinaapp二级域名的链接跳转至新域名

使用新域名前创建的页面可能已经被搜索引擎收录,如果希望通过这些链接访问网站时能自动跳转至新域名的话,就需要通过配置让这部分链接301永久重定向至新域名。通常是通过修改apache的.htaccess配置文件来实现url的重定向,但是sae并不支持.htaccess的配置,而是使用了自己的AppConfig配置来实现,我们只需要参考这篇文章中的配置修改wordpress根目录下的config.yaml文件即可。配置修改完毕后上传至sae,尝试访问一下自己之前的二级域名,看看是否能成功跳转新域名。

问题三:使用未备案的独立域名访问速度较慢

由于sae是通过中转境外的代理服务器来实现绑定未备案的独立域名的,访问速度会慢于直接通过二级域名访问。无意间发现这么一个插件,能够实现“将资源文件通过SAE二级域名进行访问,而将网站主要文件通过主域名访问,进而加快访问速度”,理论上应该是会有些许效果的,不过没有对比测试过。不过这不能从根本上解决问题,最好的方法自然是sae自身支持域名的备案(短期之内就不要幻想备案制度消失这种事情了),不过不知道还需要等多久。

哥墨迹的阅读清单(2013-06-01)

前言

人的大脑容量总是有限的,那些不常用的东西渐渐就会被挤出大脑的主存以外,其中也会包括很多有价值的内容,这些有价值的内容如果不及时索引起来,终有一天就会沦落得和那些你曾经撸过的爱情动作片一样被淡忘。因此本宅决定以后每周末把一周内看过的有记录价值的文章汇总成一个清单记在博客里(平时习惯把要看的文章保存到pocket里,周末整理起来也还算方便),顺便也正好在没有什么内容可写的时候凑个数。文章会分为“技术”和“非技术”两个主题,“技术”自然不必多说了,“非技术”的话估计就会各种无下限了。好,就这么愉快的决定了!

技术

这两篇都出自左耳朵耗子酷壳,我称之为“进击的HashMap”……第一篇文章提到的并发环境下使用HashMap导致cpu 100%可以算是Java的经典问题之一了,文章详细描述了问题发生的原因,对深入理解HashMap的实现很有帮助。接下来的这一篇则是介绍了一个由于Hash算法的“非随机性”造成的漏洞,清楚了HashMap的实现以后很容易就能理解。

这一篇是我几个月前投递到importnew的一篇译文,之所以翻出来是因为前两天有人微博私信联系我表示希望能转载,而当我重看这篇文章的时候,我发现我居然很多内容都没有印象了,简直和看别人的文章一样陌生,这就再次坚定了我要把看过的内容记录下来的想法。

非技术

前段时间阿里入股新浪微博,一时间引发媒体和评论人们对这两家公司未来的各种猜想,而我作为一名屌丝程序员(有图为证),只是由衷地为那些手上握有些许期权的新浪微博同行们感到欣慰。

说到期权,就不能不提近期热映的电影《中国合伙人》,或许有不少男同胞看完之后热血沸腾,觉得自己有一天也能成为电影片尾彩蛋中致敬的那些人物,这里我不得不泼一盆冷水给大家降降温——“《致青春》看哭的女人可以娶,《合伙人》看爽的男人不能嫁”。

这说着说着就扯到创业了,现如今互联网行业的创业团队越来越多,愿意加入创业团队的人也逐渐多了起来,这对于行业和个人的发展都是好事情。但跳出互联网这个行业,整个大的就业形势还是倾向于进国企、事业单位、考公务员,以至于类似“体制内3000,体制外15000,你怎么选”这一类的帖子层出不穷。实际上,体制内、体制外的选择没有孰对孰错一说,只是选择的工作和生活方式不同而已,实在没必要相互看不起,更不要盲目地崇拜体制内,表面的光鲜背后也是有风险的。

引用上面这篇文章并不是想证明进体制内就是错的,实际上,虽然标题有一定的倾向性,但内容里讲的多是一些下岗家庭的故事。在那个年代,很多家庭成为了被历史的车轮碾过的砂砾,是因为他们提前为我们承受了变革的苦痛,我们今天才幸免于难。这时候我应该感谢郭嘉吗?不,我更愿意同情砂砾。

简单地用Java解决topN问题

距离上次写博客有一个月了, 反省下。今天先写篇简单点的,算是热热身吧。

写在前面

我想几乎每个找过工作的程序员都曾经在面试的时候遇到过如何求topN的问题,而且多数都能不假思索的回答:求topN大用小顶堆,求topN小用大顶堆(觉得反了的同学请去面壁。。。),但是应该也有一部分同学和我之前一样,一直只是把它作为一道面试题而已。

思路

说来不怕丢人,我真的是最近才遇到需要求top N大的场景,囧。。。当时的第一反应是要自己实现一个固定大小的二叉堆,因为印象中常用的Java工具类里并没有现成的实现,但是从头开始实现一个基础工具类是件挺麻烦的事情,别的不说,单是测试就得耗费不少功夫,网上虽然有很多现有实现,但是都不见得那么靠谱。于是想了一个折中的办法就是,对现有的成熟的Java工具类做封装,以达到想要的功能。

代码实现

JDK里有一个现成的工具类叫PriorityQueue,顾名思义,实现的是优先队列的功能,它能够保证每次插入新元素后,队列首位始终是优先级最高的元素,这正是我们想要的,但是它并没有对队列的容量做限制,因此只要对这一点做些许改造,就能达到我们期望的效果。代码如下:

	//固定容量的优先队列,模拟大顶堆,用于解决求topN小的问题
	public static class FixSizedPriorityQueue>{
		private PriorityQueue queue;
		private int  maxSize; //堆的最大容量

		public FixSizedPriorityQueue(int maxSize){
			if(maxSize <= 0) throw new IllegalArgumentException();
			this.maxSize = maxSize;
			this.queue = new PriorityQueue(maxSize, new Comparator() {

				@Override
				public int compare(E o1, E o2) {
					return o2.compareTo(o1);
				}
			});
		}

		public void add(E e){
			if(queue.size() < maxSize){ //未达到最大容量,直接添加
				queue.add(e);
			}else{ //队列已满
				E peek = queue.peek();
				if(e.compareTo(peek) < 0){ //将新元素与当前堆顶元素比较,保留较小的元素
					queue.poll();
					queue.add(e);
				}
			}
		}

		public List sortedList(){
			List list = new ArrayList(queue);
			Collections.sort(list); //PriorityQueue本身的遍历是无序的,最终需要对队列中的元素进行排序
			return list;
		}

	}

注意一下构造PriorityQueue时用到的Comparator,这里面有个比较tricky的地方,因为代码本身是想实现一个大顶堆来解决求topN小的问题,而PriorityQueue的优先级定义是元素越小优先级越高,这与我们的期望相反,因此我们需要自定义PriorityQueue的Comparator,反转元素大小的定义,这样就能保证队列首位的元素是当前队列中最大的。而在比较新元素和队列首位元素的大小时,则是按照正常的元素大小定义做比较。

结尾

在需要快速实现需求的时候,我们可能没有足够的时间把代码写得很完美,如何写最少的代码实现想要的功能,也是我们日常工作中需要考虑的问题。
PS:文中若有纰漏之处,敬请拍砖。

新博客开张了!

搭建自己的独立博客,应该是每个程序员都想过的事,但是考虑到服务器成本,多少总是会有点犹豫,再加上已经有很多不错的博客平台可供选择,多数人最终都会放弃这个想法,我在这之前也经历过类似的阶段。

我的博客经历

  • 最初是在javaeye(如今改名叫iteye了)上记录些平时遇到的技术问题,当时主要是因为技术方向是java的缘故,经常在javaeye上看别人写的文章,在上面开博基本也就成了顺其自然的事情。到现在,javaeye还是我写博客最多的地方(虽然数量也没多少,囧)。
  • 之后尝试过在github page上用octopress搭建自己的博客,还特地申请了一个域名(已经过期了),但是由于不习惯于那过于geek的写作方式,最终还是放弃了。
  • 后来萌生了搭个wordpress的念头,本来准备租个vps,结果发现sae这玩意儿拿来搭wordpress貌似能省些银子,试用了一下感觉除了不能在线编辑主题和插件之外,也没有什么不方便的地方,于是就愉快的决定暂住下来了,未来如果有需要再迁到vps上面去也不迟。

写些什么

博客应该主要还是会以Java相关的技术文章为主,偶尔辅以一些个人兴趣爱好相关的吐槽,频率尽量保持在一周一篇左右。

关于博客的名称

“哥墨迹”这个昵称用了好几年了,最初是因为下面这张照片。寂寞哥

没错,左边那个表情略显呆滞的二货正是本人,当时正值单身的我从此有了“寂寞哥”这么个悲情的外号。某日突发奇想,觉得把“寂寞哥”三个字翻过来念貌似更显霸气,于是乎,“哥墨迹”就成了我在网上通用的昵称。

参考文章

WordPress和github page的选择和搭建:推荐纠结到底选择wordpress还是github page的朋友看一下。

敏捷的写博客:看过之后或许更加坚定你写博客的信念。

有趣的是,上面两篇博文的作者分别选用了wordpress和github page,阅读之余,大家也可以拿来作为一个参考。