入坑两个月,java.lang包下的Thread类
Posted kirito2924
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了入坑两个月,java.lang包下的Thread类相关的知识,希望对你有一定的参考价值。
Thread类API中的英文描述:
file:///D:/Java/JDK8/Docs/docs/api/index.html
英语不好,大致的意思是:
线程是程序执行时的线程,java虚拟机(JVM)允许一个应用运行多个线程(并发)。
每一个线程都自己的优先权,优先级高的线程会比优先级低的线程优先执行。每一个线程可以被设置成守护线程(daemon),当一段代码运行一些线程时,会创建一个Thread类的对象,这个新的线程初始的优先级是创建这个线程对象时程序员设定好的,仅且仅当用户把这个线程设定成daemon,这个线程才是daemon
当java虚拟机(JVM)开启时,这儿通常有一个非daemon线程(一般的会调用某个类的main方法),java虚拟机(JVM)会一直执行这些线程,直到出现下面几种情况:
第一种:程序调用Runtime类的退出方法,并且安全管理器允许该退出操作
第二种:除了daemon线程外的其他所有线程执行完毕(处于dead状态);线程run方法中遇到return语句;线程抛出一个run方法无法处理的异常
有两种方法来创建一个新的线程。
第一种:声明一个类,该类继承Thread,该类应该重写Thread类的run方法,通过调用start方法来启动该线程,下面给出一个列子:
1 class PrimeThread extends Thread { 2 long minPrime; 3 PrimeThread(long minPrime) { 4 this.minPrime = minPrime; 5 } 6 7 public void run() { 8 // compute primes larger than minPrime 9 . . . 10 } 11 }
第二种:声明一个类,实现Runnable接口,重写Runnable接口中的run方法,下面也给出一个例子:
1 class PrimeRun implements Runnable { 2 long minPrime; 3 PrimeRun(long minPrime) { 4 this.minPrime = minPrime; 5 } 6 public void run() { 7 // compute primes larger than minPrime 8 . . . 9 } 10 }
下面我们来看一下Thread的源代码部分:
1. Thread类实现了Runnable接口:
public class Thread implements Runnable
2. Thread类的构造器:
1 public Thread() 2 public Thread(Runnable target) 3 Thread(Runnable target, AccessControlContext acc) 4 public Thread(ThreadGroup group, Runnable target) 5 public Thread(String name) 6 public Thread(ThreadGroup group, String name) 7 public Thread(Runnable target, String name) 8 public Thread(ThreadGroup group, Runnable target, String name) 9 public Thread(ThreadGroup group, Runnable target, String name, long stackSize)
Thread类一共有9个构造器。其中第3个构造器没有public修饰,默认用default修饰,同一个包下可用,不多做说明。
通过上面9个构造器(除3)可以看出,用户在创建一个Thread类的对象时,可以设定的参数有:ThreadGroup、Runnable、name、stackSize
ThreadGroup:是java.lang包下的一个ThreadGroup类,ThreadGroup对象表示一个线程的集合,也可以包含另一个ThreadGroup对象。
Thread类init方法关于ThreadGroup部分源码:
1 Thread parent = currentThread(); 2 SecurityManager security = System.getSecurityManager(); 3 if (g == null) { 4 /* Determine if it‘s an applet or not */ 5 6 /* If there is a security manager, ask the security 7 manager what to do. */ 8 if (security != null) { 9 g = security.getThreadGroup(); 10 } 11 12 /* If the security doesn‘t have a strong opinion of the 13 matter use the parent thread group. */ 14 if (g == null) { 15 g = parent.getThreadGroup(); 16 } 17 } 18 19 /* checkAccess regardless of whether or not threadgroup 20 is explicitly passed in. */ 21 g.checkAccess();
其中currentThread()方法是获取当前运行的线程,下面写段代码做个试验:
1 public class Demo7{ 2 public static void main(String[] args){ 3 Thread t = Thread.currentThread(); 4 System.out.println(t.getName()); 5 } 6 }
System.getSecurityManager()是System类中的一个静态方法,该方法是用来返回一个SecurityManager类的对象security,下面是System类中getSecurityManager():
/** * Gets the system security interface. * * @return if a security manager has already been established for the * current application, then that security manager is returned; * otherwise, <code>null</code> is returned. * @see #setSecurityManager */ public static SecurityManager getSecurityManager() { return security; }
注释说明,如果这个安全管理已经创建security对象,则返回这个security,如果没有,则返回null,其中System类的security初始值被设置为null。
/* The security manager for the system. */ private static volatile SecurityManager security = null;
再看Thread类中init方法关于ThreadGroup部分做了什么处理?
先创建两个对象parent和security:
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
接着对用户创建Thread类对象设置的参数做一个判断,如果用户没有设定ThreadGroup参数,则传递一个null值
init方法先判断这个参数是否为null,如果不为null,则判断security是否为空,如果不为空,则获取security所在的ThreadGroup赋值给用户创建的Thread对象,即g的值
接着再判断g是否为空,如果还为null,则将当前线程的ThreadGroup赋值给g。总之,如果用户未设置g值,就把security的g值赋值给g,如果security的g值也为空,就把parent的g值赋给g。
最后再调用g(ThreadGroup)的checkAccess方法,ThreadGroup类的checkAccess方法源码:
1 public final void checkAccess() { 2 SecurityManager security = System.getSecurityManager(); 3 if (security != null) { 4 security.checkAccess(this); 5 } 6 }
可以看出该方法,其实是调用了SecurityManager对象的checkAccess(ThreadGroup g)方法:
1 public void checkAccess(ThreadGroup g) { 2 if (g == null) { 3 throw new NullPointerException("thread group can‘t be null"); 4 } 5 if (g == rootGroup) { 6 checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION); 7 } else { 8 // just return 9 } 10 }
其中rootGroup应该是g的最高级parent(未必正确),看一下源码:
1 private static ThreadGroup rootGroup = getRootGroup(); 2 private static ThreadGroup getRootGroup() { 3 ThreadGroup root = Thread.currentThread().getThreadGroup(); 4 while (root.getParent() != null) { 5 root = root.getParent(); 6 } 7 return root; 8 }
checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION)这个坑之后再填吧,挖不下去了。
Runnable:前面介绍了创建Thread类对象的两种方法,其中一种就是传入一个Runnable对象。
如果在创建一个线程时,没有传入Runnable对象,则init方法默认传入的是一个null值,来看一下源码:
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }
在看看init方法对应参数代表什么意思:
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); }
从中还可以看出,如果在创建Thread类对象时,没有指定其姓名,会默认设置名字,即Thread-加上nextThreadNum()返回值:
private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; }
所以第一个线程的名字会默认为Thread-0,第二个线程的名字为Thread-1......
stackSize:栈的大小,待补充......
3. 线程的状态
Thread类有个内部枚举类State,该类就声明了线程的几种状态:
public enum State { NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED; }
NEW:线程刚创建时具有的状态;
public class Demo8{ public static void main(String[] args){ Thread t = new Thread(); System.out.println(t.getState()); } }
RUNNABLE:是指线程正在运行,线程获取CPU的时间片
public class Demo8{ public static void main(String[] args){ Thread t = new Thread(); t.start(); System.out.println(t.getState()); } }
BLOCKED:是指程序进入阻塞状态,假如有两个线程,并且两个线程都是同步安全的(synchronized),当一个线程处于runnable状态时,则另一个线程处于blocked状态。
public class Demo8{ public static void main(String[] args){ Thread t1= new Thread(){ public void run(){ synchronized(Thread.class){ for(int i = 0; i < 100; i++){ } } } }; Thread t2 = new Thread(){ public void run(){ synchronized(Thread.class){ System.out.println(t1.getState()); } } }; t1.setPriority(1); t2.setPriority(10); t1.start(); t2.start(); } }
WAITING:程序处于等待状态,调用wait()、join()、await()、lock()等方法,都会使线程处于waiting状态,需要注意的是这些方法必须是无参数的。
public class Demo8{ public static void main(String[] args){ Thread t1 = new Thread(){ public void run(){ try{ join(); } catch(InterruptedException e){ e.printStackTrace(); } } }; Thread t2 = new Thread(){ public void run(){ System.out.println(t1.getState()); } }; t1.start(); t2.start(); } }
TIMED_WAITING:程序处于限时等待状态,调用wait()、join()、await()、lock()、sleep()等方法,都会使线程处于waiting状态,需要注意的是这些方法必须加入参数。
TERMINATED:终止状态,即线程结束
4. Thread类中部分方法源码解析
————this.start()方法
public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } }
可以看出线程是在调用start方法时加入init方法中指定的线程池的,其次线程在创建时并不是把内部的枚举类State的NEW值给这个线程,而是定义一个int型的threadStatus
变量,并且这个变量初始值为0。
private volatile int threadStatus = 0;
并且start还调用了一个本地的start0()方法,由于没看过JVM相关的知识,所以对于native修饰的方法无能无力:
private native void start0();
不过IBM有一篇文章对此讲解的比较详细,这里附上链接:https://www.ibm.com/developerworks/cn/java/j-lo-processthread/#icomments
大概的意思是java的start方法会调用 JVM_StartThread方法,而 JVM_StartThread方法会创建一个与本地相关的线程,该线程与java创建的线程有着一一对应关系。
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) … native_thread = new JavaThread(&thread_entry, sz); …
这篇文章并没有详细讲解JVM的代码实现过程,而是给出了方法调用图,该图来自上面链接的那篇文章:
————setPriority(int)方法
这里先留坑,简单讲一下,线程的优先级可以理解为线程抢占cpu时间片的概率,因此并不能保证优先级高一定会先执行
1 public class Demo2 { 2 public static void main(String[] args) { 3 Thread t1 = new Thread() { 4 @Override 5 public void run() { 6 for(int i = 0; i < 1000; i++) { 7 System.out.println("********"); 8 } 9 } 10 }; 11 Thread t2 = new Thread() { 12 @Override 13 public void run() { 14 for(int i = 0; i < 1000; i++) { 15 System.out.println("--------"); 16 } 17 } 18 }; 19 t1.setPriority(1); 20 t2.setPriority(10); 21 t1.start(); 22 t2.start(); 23 } 24 }
其次,windows操作系统的优先级为7个级别(未验证),而java的优先级分为10个级别,所以java程序1~10几个优先级中,必定有几个优先级在windows操作系统下级别是一样的,真正决定优先级的应该是本地的setPriority0()方法。
setPriority0(priority = newPriority);
—————activeCount方法,获取当前线程池中的线程数量
1 import java.lang.reflect.InvocationTargetException; 2 import java.lang.reflect.Method; 3 public class Demo3 { 4 public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 5 MyRunnable m = new MyRunnable(); 6 Thread t1 = Thread.currentThread(); 7 ThreadGroup tg = t1.getThreadGroup(); 8 Thread t2 = new Thread(m); 9 Class c = ThreadGroup.class; 10 Method declaredMethod = c.getDeclaredMethod("add", Thread.class); 11 Method method = declaredMethod; 12 method.setAccessible(true); 13 method.invoke(tg,t2); 14 System.out.println(Thread.activeCount()); 15 System.out.println(t1.getName()); 16 System.out.println(tg.getName()); 17 } 18 } 19 class MyRunnable implements Runnable{ 20 public void run() {} 21 }
这里是利用反射的机制,调用ThreadGroup类中的add(Thread t)方法,把线程添加到指定的线程池,这里要说明的是,创建的线程对象时,Thread类构造器调用的init初始化方法,并没有把线程加入指定的线程池,而是在start方法中调用了ThreadGroup的add方法。
未完待续......
以上是关于入坑两个月,java.lang包下的Thread类的主要内容,如果未能解决你的问题,请参考以下文章