今天研究了下
Java
线程基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程),以及构造器中的stackSize
.....
<!-- more -->
守护线程
估计学过Unix
开发但是没有细致学习Java
的同学们会疑惑了,操作系统里面是没有所谓的守护线程的概念,只有守护进程一说,但是Java
语言机制是构建在JVM的基础之上的,意思是Java
平台把操作系统的底层给屏蔽起来,所以它可以在它自己的虚拟的平台里面构造出对自己有利的机制,而语言或者说平台的设计者多多少少是受到Unix
思想的影响,而守护线程机制又是对JVM
这样的平台凑合,于是守护线程应运而生。
Daemon
的作用是为其他线程的运行提供服务,比如说GC
线程。其实User Thread
线程和Daemon Thread
守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开:如果User Thread
全部撤离,那么Daemon Thread
也就没啥线程好服务的了,所以虚拟机也就退出了。
守护线程并非虚拟机内部可以提供,用户也可以自行的设定守护线程,方法:setDaemon(boolean on)
但是有几点需要注意:
-
thread.setDaemon(true)
必须在thread.start()
之前设置,否则会跑出一个IllegalThreadStateException
异常,因为你不能把正在运行的常规线程
设置为守护线程
。(备注:这点与守护进程有着明显的区别,守护进程是创建后,让进程摆脱原会话的控制+让进程摆脱原进程组的控制+让进程摆脱原控制终端的控制;所以说寄托于虚拟机的语言机制跟系统级语言有着本质上面的区别)
public final void setDaemon(boolean on) {
checkAccess();
if (isAlive()) {//如果处于运行状态,抛出异常
throw new IllegalThreadStateException();
}
daemon = on;
}
- 在
Daemon
线程中产生的新线程也是Daemon
的。(这一点又是有着本质的区别了:守护进程fork()
出来的子进程不再是守护进程
,尽管它把父进程的进程相关信息复制过去了,但是子进程的进程的父进程不是init
进程,所谓的守护进程
本质上说就是父进程挂掉,init收养
) - 不是所有的应用都可以分配给
Daemon
线程来进行服务,比如读写操作
或者计算逻辑
。因为在Daemon Thread
还没来的及进行操作时,虚拟机可能已经退出了。
Thread thread = new Thread(() -> {
Thread innerThread = new Thread(() -> {
try {
for (int i = 1; i < 10; i++) {
Thread.sleep(1_000);
System.out.println("守护线程 " + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
innerThread.setDaemon(true);
innerThread.start();
try {
for (int i = 1; i < 6; i++) {
Thread.sleep(1_000);
System.out.println("常规线程 " + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
日志分析
常规线程 1
守护线程 1
守护线程 2
常规线程 2
守护线程 3
常规线程 3
守护线程 4
常规线程 4
守护线程 5
常规线程 5
从上面的日志中可以看到,如果将innerThread
设置成守护模式
,那么待当前主线程完成处理退出后,守护线程
也会随着销毁
JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台线程候一定要注意这个问题。
那么daemon Thread
实际应用在那里呢?举个例子,Web
服务器中的Servlet
,容器启动时后台初始化一个服务线程,即调度线程
,负责处理http
请求,然后每个请求过来调度线程
从线程池
中取出一个工作者线
程来处理该请求,从而实现并发控制的目的。
线程函数
在学习Thread
的时候,常见写法就是Runnable
和ThreadName
,所以这里重点讲解ThreadGroup
以及stackSize
的作用
public Thread();
public Thread(Runnable target);
public Thread(String name);
public Thread(Runnable target, String name);
public Thread(ThreadGroup group, Runnable target);
public Thread(ThreadGroup group, String name);
public Thread(ThreadGroup group, Runnable target, String name);
public Thread(ThreadGroup group, Runnable target, String name, long stackSize);
- 第一种是实例化一个无参构造函数,
ThreadName
与GroupName
为系统默认 - 第二种是实现了
Runnable
接口的类的实例,Thread
类也实现了Runnable
接口,因此,从Thread
类继承的类的实例也可以作为target
传入这个构造方法。 - 第三种是可以自定义
ThreadName
- 第五种是可以指定该线程属于哪个
ThreadGroup
(线程组) - 第八种是可以指定
堆栈大小
(比如压栈大小),这个值一般是CPU页面的整数倍,如x86的页面大小是4KB.在x86平台下,默认的线程栈大小是12KB.
ThreadName
,可以通过创建Thread
实例后调用,setName
方法设置。默认线程名:Thread-N,N是线程建立的顺序,是一个不重复的正整数,它的来源基于Thread.nextThreadNum()
/* For autonumbering anonymous threads. */
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
stackSize
是一种具有平台依赖性的参数,它能指定堆栈的大小。 在某些平台上,指定一个较高的stackSize
参数值可能使线程在抛出StackOverflowError
之前达到较大的递归
深度。stackSize
参数的值与最大递归深度和并发程度之间的关系细节与平台有关。在某些平台上,stackSize
参数的值无论如何不会起任何作用。作为建议,可以让虚拟机自由处理stackSize
参数。
ThreadGroup group = new ThreadGroup("battcn-group");
Thread t1 = new Thread(() -> System.out.println("hello my name's" + Thread.currentThread().getName() + " group name's" + Thread.currentThread().getThreadGroup().getName()));
Thread t2 = new Thread(() -> System.out.println("hello my name's" + Thread.currentThread().getName() + " group name's" + Thread.currentThread().getThreadGroup().getName()), "thread-battcn2");
Thread t3 = new Thread(() -> System.out.println("hello my name's" + Thread.currentThread().getName() + " group name's" + Thread.currentThread().getThreadGroup().getName()), "thread-battcn3");
t1.start();
t2.start();
t3.start();
group.enumerate(new Thread[]{t2, t3});
以下是上部代码片段的日志输出,可以看到指定ThreadName
与ThreadGroup
以及不指定的的区别
hello my name's Thread-0 group name'smain
hello my name's thread-battcn2 group name'smain
hello my name's thread-battcn3 group name'smain
警告 如果对
stackSize
有兴趣的可以试试下面代码,不过慎重,有可能吧你电脑内存跑完....
for (int i = 0; i < Integer.MAX_VALUE; i++) {
new Thread(group, new Runnable() {
@Override
public void run() {
try {
add(1);
} catch (Error error) {
System.out.println(count);
error.printStackTrace();
}
}
private void add(int i) {
count++;
add(i + 1);
}
}, "thread-battcn-4",1 << 24).start();
}
- 说点什么
微信公众号:battcn
(欢迎调戏)
全文代码:https://git.oschina.net/battcn/battcn-concurent
- 个人QQ:1837307557
- battcn开源群(适合新手):3916196
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。