`
2nd
  • 浏览: 15121 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

斗胆提交了一个BUG到oracle,是不是我理解的太肤浅了

 
阅读更多

前几天遇到一个关于java线程的疑惑,一直没有得到解答,甚至最后我认为这应该算是jdk的一个BUG,于是自不量力的提交了一个BUG到oracle官方,至今没有任何回复呢。

 

背景:

我做了一个定时下载某个网站图片的应用服务。

当时的想法很简单,因为每隔5分钟下载一次,一次可能需要下载多张,每个图片开一个线程去下载,并行下载完毕后线程就自动关闭了。5分钟后再开新的线程去下载,如此一直运行.......

 

问题:

按照上面的方法可以实现所有功能,但是在日志里发现一个很奇怪的问题,就是线程ID一直增加,没有减少,查阅了一下API里关于Thread的解释,里面是这样说的:

public long getId()
返回该线程的标识符。线程 ID 是一个正的 long 数,在创建该线程时生成。
线程 ID 是唯一的,并终生不变。
线程终止时,该线程 ID 可以被重新使用。

也就是说,线程Id应该是可以被回收的,一直增加是有问题的。

 

我尝试了好多方法验证,最终发现Id还是没有被重复利用,于是对API产生了一些怀疑,到官方在线查看英文的API是这样说的:

 

public long getId()

Returns the identifier of this Thread. The thread ID is a positive long number generated when this thread was created. The thread ID is unique and remains unchanged during its lifetime.

When a thread is terminated, this thread ID may be reused.

英文原版API的说法是 this thread ID may be reused.  我英语水平不是很好,may be确实可以翻译成“可以”,但是貌似不是很确定,人家的意思可能是:有可能被重复利用,也有可能不被重复利用。

我想这也是为什么提交bug到oracle没人理我的原因吧。

 

接下来看看jdk里关于Thread的实现(1.6版本):

 

http://aiiuui.com/code/read.jsp?file=%252Fjava6.0%25E6%25BA%2590%25E7%25A0%2581%252Fjava%252Flang%252FThread.java

 

访问上面的连接 查找线程id的定义: private long tid;

接着 查找 tid 的初始化 : tid = nextThreadID();

接下来查看初始化方法nextThreadID() 的实现:

private static synchronized long nextThreadID() {
	return ++threadSeqNumber;
    }


我们看到这里返回的Id只有增加,没有任何其他处理。

我们可以继续搜索对于 threadSeqNumber 的处理,看看是否有回收的处理:

最终发现只有一处定义,其他地方在没有处理  private static long threadSeqNumber;

至此,我明白为什么Thread的id只增加不减少了,但是我认为这个是不合理的,也是与API不符的,所以我斗胆提交了一个bug到oracle,至今无回应,哎。

 

本人做开发时间不久,被这个问题困扰很久,写出来希望得到大家的帮助。


1
3
分享到:
评论
18 楼 rensanning 2012-11-22  
2nd 写道
rensanning 写道
long的最大值为2的63次方减1,就是9223372036854775807,
假如1毫秒创建一个线程,1年创建的线程数为365*24*60*60*1000=31536000000
那么要达到long的最大值需要292471208.7年,也就是将近3亿年才能overflow。试想谁能做出JVM这么长时间不重启的的系统呢。

Thread ID是JVM内部级别的unique,而不是UUID,这里的reused说的是在JVM重启后,他将会重新取值。JVM级别的unique也说明它不一定会从0开始,从任意数开始都有可能。

其实,按照long的取值范围和楼上那位的说法MAX+1=MIN,实际应用中应该不会出什么问题。但是您说的jvm级别的unique,我是有些不太理解,jvm怎么重启?我现在能想到的只能重新运行程序。还有其他测试方法吗??

很少有动态启动JVM的。当使用java XXXClass的时候JVM开始启动,直到程序执行完毕。

比如Tomcat采用从org.apache.catalina.startup.Bootstrap开始启动,之后通过三层 ClassLoader(System,Common,Webapp)来加载其他的类,从而保证程序运行所需要的类都被装载到JVM中。
17 楼 rensanning 2012-11-22  
qq413041153 写道
rensanning 写道
long的最大值为2的63次方减1,就是9223372036854775807,
假如1毫秒创建一个线程,1年创建的线程数为365*24*60*60*1000=31536000000
那么要达到long的最大值需要292471208.7年,也就是将近3亿年才能overflow。试想谁能做出JVM这么长时间不重启的的系统呢。

Thread ID是JVM内部级别的unique,而不是UUID,这里的reused说的是在JVM重启后,他将会重新取值。JVM级别的unique也说明它不一定会从0开始,从任意数开始都有可能。

10楼 你是不是有点误解了 1毫秒一个线程可能会太少了,因为一个进程可以有N个线程.举个例子:
我们公司的一台linux服务器运行了17个tomcat 每个tomcat都是一个web项目,每个web项目每次访问都会创建一个线程,每个tomcat都是有了数据库连接池,还装了个oracle数据库运行了两个数据库实例,一个web请求到oracle 一个查询创建一个线程,一个请求只有一个查询的很少,...
这么算下来一个web请求总共创建的线程大概10个左右 17的tomcat服务器 总共每秒接受的请求平均1000个 一秒也就是10000个线程. 出去web请求还有其他的一些线程,比如定时任务之类的线程数量更不会少....毕竟线程不是进程...

Tomcat可以配置成多实例(JVM独立)的,像你们这样的服务器配置不应该是JVM共享的,不然系统配置该优化优化了,重点理解一下Tomcat的CATALINA_BASE的含义。“1毫秒创建一个线程”只是一个例子,当然想taobao这样的负载远远不至于这点儿。
16 楼 2nd 2012-11-22  
rensanning 写道
long的最大值为2的63次方减1,就是9223372036854775807,
假如1毫秒创建一个线程,1年创建的线程数为365*24*60*60*1000=31536000000
那么要达到long的最大值需要292471208.7年,也就是将近3亿年才能overflow。试想谁能做出JVM这么长时间不重启的的系统呢。

Thread ID是JVM内部级别的unique,而不是UUID,这里的reused说的是在JVM重启后,他将会重新取值。JVM级别的unique也说明它不一定会从0开始,从任意数开始都有可能。

其实,按照long的取值范围和楼上那位的说法MAX+1=MIN,实际应用中应该不会出什么问题。但是您说的jvm级别的unique,我是有些不太理解,jvm怎么重启?我现在能想到的只能重新运行程序。还有其他测试方法吗??
15 楼 2nd 2012-11-22  
char1st 写道
When a thread is terminated, this thread ID may be reused.


只要一个线程里的run方法执行完毕,就意味着线程结束了,其他工作交给虚拟机就可以了吧,难道要手动结束吗?我测试的时候确实没有手动结束线程操作
14 楼 2nd 2012-11-22  
yq4427 写道
这种场景的话,用线程池更合适

是的,经过这次以后发现确实应该用线程池更合适
13 楼 2nd 2012-11-22  
test_lockxxx 写道
long 最大值是多少?
去查一下。

其实你可以完全这么干:


while(true)
{
    Thread x = new Thread(.....); //不断的生成新的线程,这个线程里什么都不干,马上退出线程。

    System.out.println(x.getId());
}

你不是想证明,你的想法是否正确吗

我按照和您给出的类似方法验证过,在一个服务器上跑了48小时,ID确实是没有减少的,根据jdk里的源码也可以推断,ID不可能减少,只能增加。这与API的说法不符。。。
12 楼 2nd 2012-11-22  
ansjsun 写道
这个应该用线程池吧.....

如果没用..你看看你线程是不是没有结束..

你好,我发现了这个问题后确实改用线程池了,不过这个问题还是存在的,可能是我的用法确实有问题,不过我总觉得API写的不是那么严谨
11 楼 qq413041153 2012-11-22  
rensanning 写道
long的最大值为2的63次方减1,就是9223372036854775807,
假如1毫秒创建一个线程,1年创建的线程数为365*24*60*60*1000=31536000000
那么要达到long的最大值需要292471208.7年,也就是将近3亿年才能overflow。试想谁能做出JVM这么长时间不重启的的系统呢。

Thread ID是JVM内部级别的unique,而不是UUID,这里的reused说的是在JVM重启后,他将会重新取值。JVM级别的unique也说明它不一定会从0开始,从任意数开始都有可能。

10楼 你是不是有点误解了 1毫秒一个线程可能会太少了,因为一个进程可以有N个线程.举个例子:
我们公司的一台linux服务器运行了17个tomcat 每个tomcat都是一个web项目,每个web项目每次访问都会创建一个线程,每个tomcat都是有了数据库连接池,还装了个oracle数据库运行了两个数据库实例,一个web请求到oracle 一个查询创建一个线程,一个请求只有一个查询的很少,...
这么算下来一个web请求总共创建的线程大概10个左右 17的tomcat服务器 总共每秒接受的请求平均1000个 一秒也就是10000个线程. 出去web请求还有其他的一些线程,比如定时任务之类的线程数量更不会少....毕竟线程不是进程...
10 楼 rensanning 2012-11-21  
long的最大值为2的63次方减1,就是9223372036854775807,
假如1毫秒创建一个线程,1年创建的线程数为365*24*60*60*1000=31536000000
那么要达到long的最大值需要292471208.7年,也就是将近3亿年才能overflow。试想谁能做出JVM这么长时间不重启的的系统呢。

Thread ID是JVM内部级别的unique,而不是UUID,这里的reused说的是在JVM重启后,他将会重新取值。JVM级别的unique也说明它不一定会从0开始,从任意数开始都有可能。
9 楼 char1st 2012-11-21  
When a thread is terminated, this thread ID may be reused.
8 楼 qq413041153 2012-11-21  
支持一下搂住的探索精神
7 楼 laogao3232 2012-11-21  
其实,这样是对的,当这个id增加到最大之后,会重新开始吧。
6 楼 thinkx 2012-11-21  
其实,楼主可能不知道:
Long.MIN_VALUE = Long.MAX_VALUE + 1
5 楼 yq4427 2012-11-21  
这种场景的话,用线程池更合适
4 楼 oaklet 2012-11-21  
Quartz设置几个线程来回用就行了
3 楼 test_lockxxx 2012-11-21  
long 最大值是多少?
去查一下。

其实你可以完全这么干:


while(true)
{
    Thread x = new Thread(.....); //不断的生成新的线程,这个线程里什么都不干,马上退出线程。

    System.out.println(x.getId());
}

你不是想证明,你的想法是否正确吗
2 楼 huaye2007 2012-11-21  
你这个问题就像 数据库的主键ID为啥不回收一样。
你下载图片,一张图片就开一个线程,那要是几百万张图片,还能开几百万个线程?
一般一个进程最多几千个线程,而且开这么多线程效率不高。
这不是bug.
1 楼 ansjsun 2012-11-21  
这个应该用线程池吧.....

如果没用..你看看你线程是不是没有结束..

相关推荐

    ORACLE_DBA必备技能详解

    ORACLE_DBA必备技能详解 --个人认为这是一本初级DBA很值得一看的书 (最近积分严重不够用,小的斗胆挂个三分,诸君见谅 ^_^)

    斗胆灯产品规格书模板.docx

    斗胆灯,作为一种高效节能的照明设备,以其卓越的性能和广泛的应用范围,越来越受到市场的青睐。本文将深入解析一款1X20W斗胆灯的产品规格书,从产品描述、特性、技术参数、可靠性测试以及安装方式等方面进行全面...

    斗胆同学.py(用Python打印的一首恶搞诗)

    斗胆同学.py(用Python打印的一首恶搞诗)

    斗胆灯产品规格书模板.pdf

    斗胆灯产品规格书模板.pdf

    ChatGPT时代的机遇与挑战

     近期恰逢好友孙继伟教授邀请我参加一个活动,分享这个话题的个人看法,虽明知力有不逮,但也斗胆一试。能力有限,错误难免,欢迎大家拍砖。我计划从如下几个问题来展开说明。  2、什么是ChatGPT?  从目前来看,...

    Camel写的应用程序框架

    一个人的才智也太有限了,如果这个小生命在我一个人的手里,也许会因为得不到充足的阳光雨露而不能茁壮成长,但是如果有您的加入,多了您的呵护,相信我们的目标才更有可能实现。如果您是高手,那么就在这里表现您的...

    一个开源通用的应用程序框架 (待参与)

    一个人的才智也太有限了,如果这个小生命在我一个人的手里,也许会因为得不到充足的阳光雨露而不能茁壮成长,但是如果有您的加入,多了您的呵护,相信我们的目标才更有可能实现。如果您是高手,那么就在这里表现您的...

    一个开源通用的应用程序框架2版 (待参与)

    一个人的才智也太有限了,如果这个小生命在我一个人的手里,也许会因为得不到充足的阳光雨露而不能茁壮成长,但是如果有您的加入,多了您的呵护,相信我们的目标才更有可能实现。如果您是高手,那么就在这里表现您的...

    【zfind】简单的C/C++宏定义和类定义搜索工具

    2.将zfind.exe放到环境变量Path中任意一个路径下,一般情况下直接放到C:/windows/system32即可。 3.进入cmd命令行,并进入想要搜索的目录。比如,我的C++源文件全部在D:/src,那么CD到这个目录下,直接使用zfind ...

    届高三语文上学期期中试题扫描版.doc

    - 修辞和敬辞的使用(21题),涉及到日常交际中的礼貌用语,如“斗胆”“托福”“斧正”等,这些都是语文学习中的基础文化知识。 总的来说,这份高三语文期中试题涵盖了语言理解、文本分析、文学鉴赏、历史知识、...

    大学英语四级应用类作文的写作模版.docx

    我很[高兴/担心]了解到 [具体情况]。我写信是为了就[讨论主题]提出我的建议/提案/看法。我认为[第一点观点]。一方面,[支持第一点的理由]。另一方面,[另一点观点]。关于[具体问题],我建议[具体建议一],因为[解释...

    刘润年度演讲2022:进化的力量!

    2022,我听到最多的一个词,就是“不确定性”。 市场有不确定性。供应链有不确定性。疫情有不确定性。国际环境有不确定性。2022,似乎到处都是不确定性。今天的年度演讲,我就斗胆和你聊聊“不确定性”。 当整个...

    黄石三中上学期期中考试.doc

    【黄石三中上学期期中考试.doc】是一份高二语文试卷,主要涉及语文基础知识的考核,包括字音、字形、词语运用、病句辨析、诗词理解等多个方面。以下是对这些知识点的详细说明: 1. 字音:试题中考察了汉字的正确...

    湖北省黄石三中2020学年度上学期高二语文期中考试试卷.doc

    3. 文学文化常识:《诗经》的“六艺”、《国风》与《离骚》的文学地位、《古诗十九首》的性质,以及“风骚”一词的来源和含义,这些是语文学习中的重要文学文化知识,体现了对传统文化的理解和尊重。 4. 借物喻人的...

    海蜘蛛软路由宽带叠加双倍网速

    庆祝新中国成立 60 周年),当然如果哪位兄弟学习能力比较强推荐大名鼎鼎的 ROUTEROS (简称 ROS ), ROS 涉及到写脚本问题,这个不是一两天能够弄明白,毕竟我只是停留在应用层的一个爱好者。废话少说,举个图例 ...

    JAVA的应用标准,这个标准仅限于J2SE部分

    时常看到一些人说掌握了Java,但是让他们用Java做一个实际的项目可能又困难重重,在这里,笔者根据自己的一点理解斗胆提出自己的一些对掌握Java这个说法的标准,当然对于新手,也可以提供一个需要学习哪些内容的参考...

    高一语文下学期期中质量评估试题(扫描版)新人教版 试题.doc

    1. **语文试题结构与题型**:高一语文下学期期中质量评估试题涵盖了基础知识、文言文阅读、现代文阅读、诗歌鉴赏、写作等多个部分,全面检验学生对语文知识的掌握程度。这包括字音、字形、成语使用、语法错误辨析、...

    桌面小鬼1.0 界面辅助工具

    这个本版比上一个版本(桌面之星)添加了自动换桌面的功能,壁纸支持BMP,JPG,PNG,GIF. 现在软件的功能包括: 3D桌面宠物 定时提醒 定时关机 动态更换桌面 下一本版将添加从IE上保存到壁纸文件夹的功能,但是主要...

    java开发简单的数据库管理系统 源码 可运行

    初学数据库,突然有想法开发一个简单的数据库管理系统 开发期间遇到了一些问题 也自己学习了 好多东西 感觉自己动手做一些东西 对程序设计能力确实有很大提升. 小弟刚学java,没到半年,学 access和sql也不到半个月,...

Global site tag (gtag.js) - Google Analytics