04 Thread的方法(源代码) 和 线程的状态

 

1 .Thread中重要的属性

 1 publicclass Thread implements Runnable { //继承自Runnable接口private char    name[];   // 以char数组保存线程的名字
 2     private int     priority; // 线程优先级别
 3     /* Whether or not the thread is a daemon thread. */
 4     private boolean    daemon = false;  //是否为守护线程
 5     /* What will be run. */
 6     private Runnable target;  //构造方法中传递一个Runnable对象  最终由target指向
 7
 8     /* The group of this thread */
 9     private ThreadGroup    group; //线程组
10
11    //预先定义好的优先级
12    public final static int MIN_PRIORITY = 1;
13    public final static int NORM_PRIORITY = 5;
14    public final static int MAX_PRIORITY = 10;
15
16    // 这个类是在ThreadLocal中定义  类似于Map的key-value的数据结构(后面会有该类的叙述)
17    // 特殊之处:key的值是固定的 就是当前线程
18    ThreadLocal.ThreadLocalMap threadLocals = null;
19
20     // 当不为线程命名的时候 默认名称是Thread-编号 编号从0开始增长 就是依靠这个 前面已经详细讲述
21     private static int threadInitNumber;
22     private static synchronized int nextThreadNum() {
23       return threadInitNumber++;
24     }
27    ... ...
26 }

2 构造方法

在Thread重载了很多构造方法 我们挑选几个常用的进行列举

 1   public Thread() {
 2       init(null, null, "Thread-" + nextThreadNum(), 0);
 3   }
 4
 5  public Thread(String name) {
 6     init(null, null, name, 0);
 7  }
 8
 9   public Thread(Runnable target) {
10        init(null, target, "Thread-" + nextThreadNum(), 0);
11   }
12
13  public Thread(Runnable target, String name) {
14      init(null, target, name, 0);
15  }

可以看出Thread的构造方法最终都会调用init方法

 1
 2   //4个参数分别表示 线程所属组 Runnable对象  线程名字 线程栈大小
 3   //一般只会用到第2、3个参数
 4   private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
 5
 6     Thread parent = currentThread(); //获取当前运行的线程为父线程  一些属性将会基础自parent
 7     SecurityManager security = System.getSecurityManager();
 8     if (g == null) {
 9         if (security != null) {
10         g = security.getThreadGroup();
11         }
12         if (g == null) {
13         g = parent.getThreadGroup();
14         }
15     }
16     g.checkAccess();
17
18     if (security != null) {
19         if (isCCLOverridden(getClass())) {
20             security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
21         }
22     }
23     g.addUnstarted();
24
25
26     this.group = g; //指定要开启线程的组
27     this.daemon = parent.isDaemon();//指定要开启线程是否为守护线程  来自于parent
28     this.priority = parent.getPriority();//设置优先级的值 来自于parent
29     this.name = name.toCharArray();//设置线程名字
30     if (security == null || isCCLOverridden(parent.getClass()))
31         this.contextClassLoader = parent.getContextClassLoader();
32     else
33         this.contextClassLoader = parent.contextClassLoader;
34     this.inheritedAccessControlContext = AccessController.getContext();
35     this.target = target;//设置要执行的目标 Runnable对象
36     setPriority(priority);//设置优先级
37         if (parent.inheritableThreadLocals != null)
38         this.inheritableThreadLocals =
39         ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
40         /* Stash the specified stack size in case the VM cares */
41         this.stackSize = stackSize;
42
43         /* Set thread ID */
44         tid = nextThreadID();
45
46         this.me = this;
47     }

3 线程的状态

1 /* Java thread status for tools,
2 * initialized to indicate thread ‘not yet started‘
3 */
4 private int threadStatus = 0;  //描述线程状态的属性

线程有四种状态

  1. 新状态:线程已被创建但尚未执行(start() 尚未被调用)。

  2. 可执行状态:线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该线程,从而使得它执行。

  3. 死亡状态:正常情况下 run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。

  4. 阻塞状态:线程不会被分配 CPU 时间,无法执行。

这些状态 Thread中是以枚举来描述的:

1     public enum State {
2         NEW,
3         RUNNABLE,
4         BLOCKED,
5         WAITING,
6         TIMED_WAITING,
7         TERMINATED;
8     }

Thread中的不同方法的执行 会使线程进入不同的状态 如图:

将blocked、waiting、time waiting统称为阻塞状态,这个也是可以的

只不过这里我想将线程的状态和Java中的方法调用联系起来,所以将waiting和time waiting两个状态分离出来。

4 Thread中的成员方法

1)start方法

  start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

一个线程只能start 1次  以为一个只需要分配一次资源就够了  如果启动多次 就会出错:非法的线程状态异常 IllegalThreadStateException

 1     public synchronized void start() {
 2         if (threadStatus != 0 || this != me)  //只有处于未开启状态的线程才可以继续执行
 3             throw new IllegalThreadStateException();
 4         group.add(this);
 5         start0();
 6         if (stopBeforeStart) {
 7         stop0(throwableFromStop);
 8     }
 9     }
10
11     private native void start0();
12     private native void stop0(Object o);

2)run方法

run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。

注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

如果我们覆盖了run方法 就会执行我们覆盖的方法

如果我们向Thread传递了一个Runnable对象 就会执行该对象的run方法

1 public void run() {
2     if (target != null) { //判断是否有Runnable对象传入
3          target.run();
4     }
5 }

那么请思考下面程序的输出:

 1     public static void main(String[] args) {
 2         new Thread(new Runnable() {
 3
 4             @Override
 5             public void run() {
 6                 System.out.println("run in Runnable");
 7             }
 8
 9         }) {
10             public void run() {
11                 System.out.println("run in Thread");
12             }
13         }.start();
14     }

答案:run in Thread

这就表明既复写run方法又传递Runnable时执行的是覆盖的方法

因为执行原有的run方法已经失效  根本不会判断if (target != null)  更不会执行target.run();

3)sleep方法

sleep方法有两个重载版本:

sleep(long millis)     //参数为毫秒

sleep(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒

sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。

但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。

注意,如果调用了sleep方法,必须捕获InterruptedException异常或者将该异常向上层抛出。

当线程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于让线程进入阻塞状态。

4)yield方法

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。

但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。

注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

5)join方法join方法有三个重载版本:

join()

join(long millis)     //参数为毫秒

join(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒

假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。

如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的时间。

6)interrupt方法

interrupt,顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,

也就说,它可以用来中断一个正处于阻塞状态的线程直接调用interrupt方法不能中断正在运行中的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。

但是一般情况下不建议通过这种方式来中断线程,一般会在MyThread类中增加一个属性 isStop来标志是否结束while循环,然后再在while循环中判断isStop的值。 

那么就可以在外面通过调用setStop方法来终止while循环。

 1 class MyThread extends Thread{
 2
 3         private volatile boolean isStop = false;
 4
 5         public void run() {
 6             int i = 0;
 7             while(!isStop){
 8                 i++;
 9             }
10         }
11
12         public void setStop(boolean stop){
13             this.isStop = stop;
14
15         }
16     }

7)stop方法

stop方法已经是一个废弃的方法,它是一个不安全的方法。

因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。所以stop方法基本是不会被用到的。

8)destroy方法destroy方法也是废弃的方法。基本不会被使用到。

9)以下是关系到线程属性的几个方法:

  1)getId  用来得到线程ID

  2)getName和setName       用来得到或者设置线程名称。

  3)getPriority和setPriority   用来获取和设置线程优先级。

  4)setDaemon和isDaemon  用来设置线程是否成为守护线程和判断线程是否是守护线程。

守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。

举个简单的例子:

如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。

而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

(10)Thread类有一个比较常用的静态方法currentThread()用来获取当前线程。

(11)holdsLock 判断线程是否有锁  Returns true if and only if the current thread holds the monitor lock on the specified object.



参考:

Java并发编程:Thread类的使用

http://www.cnblogs.com/dolphin0520/p/3920357.html

时间: 2024-08-06 01:03:23

04 Thread的方法(源代码) 和 线程的状态的相关文章

注意!你的Thread.Abort方法真的让线程停止了吗?

大家都知道在C#里面,我们可以使用 Thread.Start方法来启动一个线程,当我们想停止执行的线程时可以使用Thread.Abort方法来强制停止正在执行的线程,但是请注意,你确定调用了Thread.Abort方法后线程就立刻停止了吗? 答案是:不是! 下面我们来解释一下Abort方法是如何工作的.因为公用语言运行时管理了所有的托管的线程,同样它能在每个线程内抛出异常.Abort方法能在目标线程中抛出一个ThreadAbortException异常从而导致目标线程的终止.不过Abort方法被

java基础知识回顾之java Thread类学习(十)--线程的状态以及转化使用的方法介绍

   线程的概述:         线程是程序的多个执行路径,执行调度的单位,依托于进程存在.线程不仅可以共享进程的内存,而且还拥有一个属于自己的内存空间,这段内存空间叫做线程栈,是建立线程的时候由系统分配的,主要用来保存线程内部的数据,如线程执行函数中定义的变量.   java中多线程是一种抢占机制而不是分时机制.抢占机制是指CPU资源师被多个线程所共享,多个线程处于可运行状态,但是只允许一个线程在运行,他们通过竞争的方式抢占CPU.可以参考java 进程与线程的区别  线程的状态: 新生状态

多线程总结二:线程的状态转换

转自:http://zy19982004.iteye.com/blog/1626916 一.线程的状态图 二.初始状态 实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态 三.可运行状态 可运行状态只是说你资格运行,调度程序没有挑选到你,你就永远是可运行状态. 调用线程的start()方法,此线程进入可运行状态. 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入可运行状态. 当前线

java线程的状态及操作方法

一.线程的状态 1. 新建状态 在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的内存空间和其它资源,但还处于不可运行状态.新建一个线程对象可采用线程构造方法来实现. 例如:Thread thread=new Thread(); 2. 就绪状态 新建线程对象后,调用该线程的start()方法就可以启动线程.当线程启动时,线程进入就绪状态.此时,线程将进入线程队列排队,等待CPU服务,这表明它已经具备了运行条件. 3. 运行状态 当就绪状态的线程被调用并获得

Java多线程编程7--拾遗增补--线程的状态(new,runnable,terminated,timed_waiting,waiting,blocked)

线程对象在不同的运行时期有不同的状态,状态信息就存在于Thread内部类的State枚举类中 public enum State { /** * new状态是线程实例化后还从未执行start()方法时的状态 */ NEW, /** * runnable状态是线程进人运行的状态 */ RUNNABLE, /** * blocked状态出现在某一个线程在等待锁的时候. */ BLOCKED, /** * waiting是线程执行了Object.wait()方法后所处的状态 */ WAITING, /

iOS多线程开发小demo3,线程的状态

// Created by dyf on 14-6-23. // Copyright (c) 2014年 ___FULLUSERNAME___. All rights reserved. // #import "DYFViewController.h" @interface DYFViewController () @property (nonatomic, strong) NSThread *thread; @end @implementation DYFViewController

多线程 线程的状态

//  Created by dyf on 14-6-23. //  Copyright (c) 2014年 ___FULLUSERNAME___. All rights reserved. // #import "DYFViewController.h" @interface DYFViewController () @property (nonatomic, strong) NSThread *thread; @end @implementation DYFViewControll

从Thread.start()方法看Thread源码,多次start一个线程会怎么样

这篇文章作为Thread类源码剖析的补充,从一个侧面来看Thread源码.也解答了面试高频问题:"多次start一个线程会怎么样?" 答案是:java.lang.IllegalThreadStateException   线程状态非法异常   继承关系是:--->extends IllegalArgumentException--->extends RuntimeException一个运行时异常,下面我们从源码来透彻分析一下start()时做了什么. 1 /** 2 * C

C# Thread.Abort方法与ThreadAbortException异常(取消线程与异常处理)

1.Abort当前线程,后续程序不会执行 class Program { public static Thread thread1; static void Main(string[] args) { thread1 = new Thread(Method1); thread1.Start(); Console.ReadKey(); } public static void Method1() { try { for (int i = 0; i < 10; i++) { Console.Writ