- 创建Java线程
Java中Thread类采用了模板设计模式,像下面的方式创建一个线程,不会做任何操作,在启动之后会迅速退出。
// 创建一个Thread对象,但并没有创建一个线程
Thread thread = new Thread();
// 启动线程
thread.start();
Thread类中的run方法做为执行单元,一般在创建具体的业务线程的时候都必须要重写run方法,以实现真正的业务逻辑。
引入策略模式(Runnable接口)使得业务逻辑与线程的控制逻辑分离后,业务逻辑更加清晰。所以大多数情况都会采用实现Runnable的方式实现Java的多线程。
public class MyRunnable implements Runnable {
public void run() {
System.out.println("here is the system logic");
}
}
Thread thread = new Thread(new MyRunnable());
thread.start();
-
Thread 的构造函数
Thread API.jpg
从上面可以看到Thread的构造函数,主要有Runnable(业务逻辑),ThreadGroup(线程组),name(线程名)以及stackSize(线程栈)四个参数。最主要的参数业务逻辑并不需要多做任何介绍,线程组和线程名两个参数有必要进行一下介绍。
2.1 线程名
为一个业务线程取一个合适的名字,当业务在出现问题的时候可以快速定位到问题,那么如果线程没有取名的话,默认线程会以”Thead-“做为前缀和一个自增的自然数做为线程名(注意是线程调用start方法的顺序递增,而不是创建Thread对象的顺序)。
线程名的修改,在线程启动之前是可以通过setName方法修改的,不过在线程启动之后,线程名将不能再修改。
2.2 ThreadGroup
在介绍ThreadGoup之前,首先要介绍一下Thread的父子关系,通过分析Thread类的init方法的源码我们可以发现,Thread的parent为创建线程的线程做为父线程;如果在创建线程之前没有指定ThreadGroup时,会将父线程的ThreadGroup做为当前线程的ThreadGroup;子线程将父线程的priority和daemon做为自己的priority和daemon。
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
- Deamon 线程
在搞清楚Deamon之前,首先要搞清楚一个问题:JVM虚拟机会在什么时候退出?
在JVM虚拟机官方文档中: The JVM will exits when the only threads running are all daemon threads. (当虚拟机的所有线程只有守护线程时,虚拟机将会退出。)
3.1 怎样创建一个daemon thread
在线程调用start之前,通过setDaemon(true)方法可以将线程设置为守护线程。注意在线程启动之后,调用setDaemon会抛出异常。通过isDaemon方法可以判断这个线程是否是守护线程。
以上仅仅是线程的最基本的属性,在业务中使用多线程,我们还需要进一步学习线程的更多特性和API。
网友评论