JAVA编程中Thread类

来源:互联网 发布:淘宝视频连接不是私密 编辑:程序博客网 时间:2024/05/24 00:32

常用方法

static Thread currentThread() 返回对当前正在执行的线程对象的引用。 
long getId()返回该线程的标识符。 
String getName()返回该线程的名称。 
int getPriority() 返回线程的优先级。 
void interrupt() 中断线程。 
boolean isAlive()测试线程是否处于活动状态。 
void join()等待该线程终止。 
void join(long millis)等待该线程终止的时间最长为 millis 毫秒。 
void join(long millis, int nanos)等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。 
void setDaemon(boolean on)将该线程标记为守护线程或用户线程。 
void setPriority(int newPriority)更改线程的优先级。 
static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 
static void sleep(long millis, int nanos)在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 
void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 
static void yield()暂停当前正在执行的线程对象,并执行其他线程。

join

当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。

public class Test {    public static void main(String[] args) {        Thread thread = new MyRunner3();        thread.start();        try {            //主线程等待thread的业务处理完了之后再向下运行              //thread和主线程合并了              thread.join();        } catch (InterruptedException e) {            // TODO Auto-generated catch block              e.printStackTrace();        }        for(int i = 0; i < 100; i++){            System.out.println("main : " + i);        }    }}class MyRunner3 extends Thread {    @Override    public void run() {        for(int i = 0; i < 5; i++){            System.out.println("i am " + getName());            try {                sleep(1000);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

sleep-线程睡眠

如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法来实现。

当当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间内,该线程不会获得执行机会,即使系统中没有其他可执行线程,处于sleep()中的线程也不会执行,因此sleep()方法常用来暂停程序的执行

yield-线程让步

yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入到就绪状态。即让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行。

实际上,当某个线程调用了yield()方法之后,只有优先级与当前线程相同或者比当前线程更高的处于就绪状态的线程才会获得执行机会。

sleep()与yield()方法区别:

1、sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给优先级高或者相同的线程机会

2、sleep()方法会将线程转入到阻塞状态,知道经过阻塞时间才会转入就绪状态;而yield()不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。因此完全有可能某个线程调用了yield()方法暂停之后,立即再次获取处理器资源被执行。

3、sleep()方法声明抛出InterruptedException异常,所以嗲用sleep()方法时要么捕捉该异常,要么显示声明抛出该异常;而yield()方法则没有声明抛出任何异常。

4、sleep()方法比yield()方法更好的可移植性,通常不建议使用yield()方法来控制并发线程执行。

isAlive

public class Test {    public static void main(String args[]) throws IllegalThreadStateException {        MyThread m = new MyThread();  //实例化Runnable子类对象        Thread t = new Thread(m, "自定义线程");        System.out.println("线程执行前:" + t.isAlive());       //false        t.start();        //isAlive(),判断线程是否处于活动状态        System.out.println("线程启动之后:" + t.isAlive());  //true    }}class MyThread implements Runnable  //实现Runnable接口{    public void run() {  //覆写run 方法//currentThread().getName()取得当前正在运行的线程的名称        System.out.println(Thread.currentThread().getName() + "运行");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

线程的优先级

线程的优先级用数字表示,范围从1到10,默认的是为5 
每个线程默认的优先级与创建它的父线程的优先级相同 
优先级越高的线程,被执行的顺序就比较靠前,在Thread中存在三个常量:MAX_PRIORITY、

MIN_PRIORITY、NORM_PRIORITY

class MyThread implements Runnable  //实现Runnable接口{    public void run() {        System.out.println(Thread.currentThread().getName());    }}public class Test {    public static void main(String args[]) {        Thread t1 = new Thread(new MyThread(), "线程A");        Thread t2 = new Thread(new MyThread(), "线程B");        Thread t3 = new Thread(new MyThread(), "线程C");        t1.setPriority(Thread.MAX_PRIORITY);        t2.setPriority(Thread.NORM_PRIORITY);        t3.setPriority(Thread.MIN_PRIORITY);        t1.start();        t2.start();        t3.start();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

后台线程

在Java程序中,只要前台有一个线程在运行,则整个Java进程都不会消失,所以此时可以设置一个后台线程,这样即使Java进程结束了,此后台线程依然会执行。要想实现这样的操作,直接使用setDaemon()方法即可。

class MyThread implements Runnable{ // 实现Runnable接口    public void run(){  // 覆写run()方法        while(true){            System.out.println(Thread.currentThread().getName() + "在运行。") ;        }    }};public class Test{    public static void main(String args[]){        MyThread mt = new MyThread() ;  // 实例化Runnable子类对象        Thread t = new Thread(mt,"线程");     // 实例化Thread对象        t.setDaemon(true) ; // 此线程在后台运行        t.start() ; // 启动线程    }};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

线程的中断

一个线程可以被另一个线程中断其操作的状态,使用interrupt()方法。

class MyThread implements Runnable  //实现Runnable接口{    public void run() {  //覆写run 方法        try {            //sleep方法会出现异常            System.out.println("1、进入run方法");            Thread.sleep(6000);     //程序会暂停1000毫秒再执行            System.out.println("2、已经完成了休眠");        } catch (InterruptedException e) {            System.out.println("3、休眠被终止!");            return;     //返回方法调用处        }        System.out.println("4、run方法正常结束");    }}public class Test {    public static void main(String args[]) {        MyThread m = new MyThread();  //实例化Runnable子类对象        Thread t = new Thread(m, "自定义线程");        t.start();        try {            //sleep方法会出现异常            Thread.sleep(100);        } catch (InterruptedException e) {        }        t.interrupt();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

终止线程

有三种方法可以使终止线程。 
1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。 
2. 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。 

3. 使用interrupt方法中断线程。



---------------------------------------------------------------------------------------------------------------------------------------------------------


在学习Thread类之前,先介绍与线程相关知识:线程的几种状态、上下文切换,然后接着介绍Thread类中的方法的具体使用。

  以下是本文的目录大纲:

  一.线程的状态

  二.上下文切换

  三.Thread类中的方法

  若有不正之处,请多多谅解并欢迎批评指正。

  请尊重作者劳动成果,转载请标明原文链接:

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

一.线程的状态

  在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类中的方法的理解。

  线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。

  当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,在前面的JVM内存区域划分一篇博文中知道程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。

  当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。

  线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。

  当由于突然中断或者子任务执行完毕,线程就会被消亡。

  下面这副图描述了线程从创建到消亡之间的状态:

  在有些教程上将blocked、waiting、time waiting统称为阻塞状态,这个也是可以的,只不过这里我想将线程的状态和Java中的方法调用联系起来,所以将waiting和time waiting两个状态分离出来。

二.上下文切换

  对于单核CPU来说(对于多核CPU,此处就理解为一个核),CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换(对于进程也是类似)。

  由于可能当前线程的任务并没有执行完毕,所以在切换时需要保存线程的运行状态,以便下次重新切换回来时能够继续切换之前的状态运行。举个简单的例子:比如一个线程A正在读取一个文件的内容,正读到文件的一半,此时需要暂停线程A,转去执行线程B,当再次切换回来执行线程A的时候,我们不希望线程A又从文件的开头来读取。

  因此需要记录线程A的运行状态,那么会记录哪些数据呢?因为下次恢复时需要知道在这之前当前线程已经执行到哪条指令了,所以需要记录程序计数器的值,另外比如说线程正在进行某个计算的时候被挂起了,那么下次继续执行的时候需要知道之前挂起时变量的值时多少,因此需要记录CPU寄存器的状态。所以一般来说,线程上下文切换过程中会记录程序计数器、CPU寄存器状态等数据。

  说简单点的:对于线程的上下文切换实际上就是 存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。

  虽然多线程可以使得任务执行的效率得到提升,但是由于在线程切换时同样会带来一定的开销代价,并且多个线程会导致系统资源占用的增加,所以在进行多线程编程时要注意这些因素。

三.Thread类中的方法

  通过查看java.lang.Thread类的源码可知:

  

  Thread类实现了Runnable接口,在Thread类中,有一些比较关键的属性,比如name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字,priority表示线程的优先级(最大值为10,最小值为1,默认值为5),daemon表示线程是否是守护线程,target表示要执行的任务。

  下面是Thread类中常用的方法:

  以下是关系到线程运行状态的几个方法:

  1)start方法

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

  2)run方法

  run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

  3)sleep方法

  sleep方法有两个重载版本:

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

 

 

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

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

复制代码
public class Test {         private int i = 10;    private Object object = new Object();         public static void main(String[] args) throws IOException  {        Test test = new Test();        MyThread thread1 = test.new MyThread();        MyThread thread2 = test.new MyThread();        thread1.start();        thread2.start();    }               class MyThread extends Thread{        @Override        public void run() {            synchronized (object) {                i++;                System.out.println("i:"+i);                try {                    System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");                    Thread.currentThread().sleep(10000);                } catch (InterruptedException e) {                    // TODO: handle exception                }                System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");                i++;                System.out.println("i:"+i);            }        }    }}
复制代码

 

 

   输出结果:

  

  从上面输出结果可以看出,当Thread-0进入睡眠状态之后,Thread-1并没有去执行具体的任务。只有当Thread-0执行完之后,此时Thread-0释放了对象锁,Thread-1才开始执行。

  注意,如果调用了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方法,则等待一定的事件。

  看下面一个例子:

复制代码
public class Test {         public static void main(String[] args) throws IOException  {        System.out.println("进入线程"+Thread.currentThread().getName());        Test test = new Test();        MyThread thread1 = test.new MyThread();        thread1.start();        try {            System.out.println("线程"+Thread.currentThread().getName()+"等待");            thread1.join();            System.out.println("线程"+Thread.currentThread().getName()+"继续执行");        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }          class MyThread extends Thread{        @Override        public void run() {            System.out.println("进入线程"+Thread.currentThread().getName());            try {                Thread.currentThread().sleep(5000);            } catch (InterruptedException e) {                // TODO: handle exception            }            System.out.println("线程"+Thread.currentThread().getName()+"执行完毕");        }    }}
复制代码

 

 

   输出结果:

  

  可以看出,当调用thread1.join()方法后,main线程会进入等待,然后等待thread1执行完之后再继续执行。

  实际上调用join方法是调用了Object的wait方法,这个可以通过查看源码得知:

  

  wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。

  由于wait方法会让线程释放对象锁,所以join方法同样会让线程释放对一个对象持有的锁。具体的wait方法使用在后面文章中给出。

  6)interrupt方法

  interrupt,顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。

  下面看一个例子:

复制代码
public class Test {         public static void main(String[] args) throws IOException  {        Test test = new Test();        MyThread thread = test.new MyThread();        thread.start();        try {            Thread.currentThread().sleep(2000);        } catch (InterruptedException e) {                     }        thread.interrupt();    }          class MyThread extends Thread{        @Override        public void run() {            try {                System.out.println("进入睡眠状态");                Thread.currentThread().sleep(10000);                System.out.println("睡眠完毕");            } catch (InterruptedException e) {                System.out.println("得到中断异常");            }            System.out.println("run方法执行完毕");        }    }}
复制代码

 

 

   输出结果:

  

  从这里可以看出,通过interrupt方法可以中断处于阻塞状态的线程。那么能不能中断处于非阻塞状态的线程呢?看下面这个例子:

复制代码
public class Test {         public static void main(String[] args) throws IOException  {        Test test = new Test();        MyThread thread = test.new MyThread();        thread.start();        try {            Thread.currentThread().sleep(2000);        } catch (InterruptedException e) {                     }        thread.interrupt();    }          class MyThread extends Thread{        @Override        public void run() {            int i = 0;            while(i<Integer.MAX_VALUE){                System.out.println(i+" while循环");                i++;            }        }    }}
复制代码

 

 

   运行该程序会发现,while循环会一直运行直到变量i的值超出Integer.MAX_VALUE。所以说直接调用interrupt方法不能中断正在运行中的线程。

  但是如果配合isInterrupted()能够中断正在运行的线程,因为调用interrupt方法相当于将中断标志位置为true,那么可以通过调用isInterrupted()判断中断标志是否被置位来中断线程的执行。比如下面这段代码:

复制代码
public class Test {         public static void main(String[] args) throws IOException  {        Test test = new Test();        MyThread thread = test.new MyThread();        thread.start();        try {            Thread.currentThread().sleep(2000);        } catch (InterruptedException e) {                     }        thread.interrupt();    }          class MyThread extends Thread{        @Override        public void run() {            int i = 0;            while(!isInterrupted() && i<Integer.MAX_VALUE){                System.out.println(i+" while循环");                i++;            }        }    }}
复制代码

 

 

   运行会发现,打印若干个值之后,while循环就停止打印了。

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

复制代码
class MyThread extends Thread{        private volatile boolean isStop = false;        @Override        public void run() {            int i = 0;            while(!isStop){                i++;            }        }                 public void setStop(boolean stop){            this.isStop = stop;        }    }
复制代码

 

 

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

  7)stop方法

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

  8)destroy方法

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

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

  1)getId

  用来得到线程ID

  2)getName和setName

  用来得到或者设置线程名称。

  3)getPriority和setPriority

  用来获取和设置线程优先级。

  4)setDaemon和isDaemon

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

  守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

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

  在上面已经说到了Thread类中的大部分方法,那么Thread类中的方法调用到底会引起线程状态发生怎样的变化呢?下面一幅图就是在上面的图上进行改进而来的:

 


0 0
原创粉丝点击