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方法。

未完待续......

转载于:https://www.cnblogs.com/kirito2924/p/9147630.html

Thread类学习(一)相关推荐

  1. java基础知识回顾之java Thread类学习(七)--java多线程安全问题(死锁)

    死锁:是两个或者两个以上的线程被无限的阻塞,线程之间互相等待所需资源. 线程死锁产生的条件: 当两个线程相互调用Join()方法. 当两个线程使用嵌套的同步代码块的时候,一个线程占用了另一个线程的锁, ...

  2. java同步锁售票_Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)...

    学习多线程之前,我们先要了解几个关于多线程有关的概念. 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是 ...

  3. java多线程阶乘_【java学习记录】11.用多线程实现阶乘的计算过程和结果的读取(thread类ReadThread )...

    (源码在上传的压缩包"[java学习记录]11.多线程实现阶乘计算(Thread类和Runnable接口)"中可看到,该压缩包可下载)java * 采用Thread类的子类实现线程 ...

  4. extends thread java_java学习之- 线程继承Thread类

    标签(空格分隔): 线程 在java.lang包中有个Thread子类,大家可以自行查阅文档,及范例: 如何在自定义的代码中,自定义一个线程呢? 1.通过对api的查找,java已经提供了对线程这类事 ...

  5. JavaSE学习52:细说多线程之Thread类和Runable接口

    一线程创建的两种方式比较 线程创建和启动有两种方式,这里只是列出步骤,不再进行详细解释. (1)继承Thread类 [java] view plaincopy print? class MyThrea ...

  6. 【高并发】一个工作了7年的朋友去面试竟然被Thread类虐的体无完肤

    前言 最近和一个工作了7年的朋友聊天,他跟我说起了他去XXX公司面试的情况,面试官的一个问题把他打懵了!竟然问他:你经常使用Thread类创建线程,那你看过Thread类的源码吗?Thread类创建线 ...

  7. 【高并发】又一个朋友面试栽在了Thread类的stop()方法和interrupt()方法上!

    来自:冰河技术 写在前面 新一轮的面试已经过去,可能是疫情的原因吧,很多童鞋纷纷留言说今年的面试题难度又提高了,尤其是对并发编程的知识.我细想了下,也许有那么点疫情的原因吧,但无论面试的套路怎么变,只 ...

  8. Java并发编程:Thread类的使用

    为什么80%的码农都做不了架构师?>>>    Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学 ...

  9. Java Thread类的使用

    From:http://www.cnblogs.com/dolphin0520/p/3920357.html 一.线程的状态 在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态, ...

最新文章

  1. R语言使用ggplot2包使用geom_violin函数绘制分组小提琴图(分组颜色配置、位置配置)实战
  2. canvas——橡皮筋式线条绘图应用
  3. WEB开发者八项注意
  4. C语言在VS2017环境下写俄罗斯方块的感悟
  5. (73)分析 KeInitializeApc ,了解 KAPC 的初始化
  6. Java初级学习(转载http://www.cnblogs.com/biehongli/p/5737612.html)
  7. CodeForces - 600E Lomsat gelral(树上启发式合并)
  8. CentOS HarBor安装与配置
  9. 关于电脑周边配件的问题
  10. AKKA Inbox收件箱
  11. oracle中not or 用法
  12. Android Realm数据库
  13. 网站开发之HTML基础表格Table和表单Form(三)
  14. python字体类型arial_python 更改字体_更改字体名称而不更改默认字体python
  15. iphone隔空投送android设备,苹果手机隔空投送怎么使用?iPhone隔空投送功能使用教程介绍[多图]...
  16. Citrix PVS架构和工作原理
  17. [算法题]返回数组A的元素组成的小于n的最大数
  18. 人工智能已经成为全球新一轮科技革命和产业变革的核心驱动力
  19. 美国本土四十八个州府48个州府所在城市的TSP旅行商回路17110km
  20. 信息安全数学基础——模重复平方计算法(两种方法实现C+JAVA)

热门文章

  1. 如何对linux文件进行编译,linux编译和执行命令都有哪些?该如何实现?
  2. 神经网络中的 Dropout 以及变体方法
  3. android image设置adjustviewbounds_探索 Android 平台的 CameraX
  4. Redis的常用功能
  5. 昨晚通宵把服务器格式化,重新配置环境。
  6. jquery通过attr取html里自定义属性原来这么方便啊
  7. 浏览器获取正确的scrollTop值
  8. emacs中安装markdown-mode
  9. Zend Framework(一)概述介绍
  10. mysql 判断质数_java之判断输入的数是否为素数