java线程介绍(三)

来源:互联网 发布:方正排版印刷软件 编辑:程序博客网 时间:2024/05/17 06:28

1.简介

上一篇文章介绍继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future的具体实现,下面将介绍线程的常见方法。

2.Thread 常用方法

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()暂停当前正在执行的线程对象,并执行其他线程。

2.1.join

当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。
例如,线程主需要依赖线程子的结果,则线程子必须在线程主结束之前结束

  • 不加join
package com.thread;/** * 描述:继承thread类 * 作者:袁伟倩 * 创建日期:2017-06-02 20:35. */public class ThreadTest extends Thread {    // 构造有参构造函数    public ThreadTest(String name){        super(name);    }    @Override    public void run() {        System.out.println(Thread.currentThread().getName() + " 线程运行开始!");        for (int i = 0; i < 5; i++) {            System.out.println(Thread.currentThread().getName() + " " + i);        }        System.out.println(Thread.currentThread().getName() + " 线程运行结束!");    }    public static void main(String[] args) throws InterruptedException {        System.out.println(Thread.currentThread().getName()+"主线程运行开始!");        ThreadTest threadTesta = new ThreadTest("子线程a");        ThreadTest threadTestb = new ThreadTest("子线程b");        threadTesta.start();        threadTestb.start();        System.out.println(Thread.currentThread().getName()+"主线程运行结束!");    }}

执行结果会出现子先结束的情况:

这里写图片描述
- 加入join

package com.thread;/** * 描述:继承thread类 * 作者:袁伟倩 * 创建日期:2017-06-02 20:35. */public class ThreadTest extends Thread {    // 构造有参构造函数    public ThreadTest(String name){        super(name);    }    @Override    public void run() {        System.out.println(Thread.currentThread().getName() + " 线程运行开始!");        for (int i = 0; i < 5; i++) {            System.out.println(Thread.currentThread().getName() + " " + i);        }        System.out.println(Thread.currentThread().getName() + " 线程运行结束!");    }    public static void main(String[] args) throws InterruptedException {        System.out.println(Thread.currentThread().getName()+"主线程运行开始!");        ThreadTest threadTesta = new ThreadTest("子线程a");        ThreadTest threadTestb = new ThreadTest("子线程b");        threadTesta.start();        threadTestb.start();        threadTestb.join();        threadTesta.join();        System.out.println(Thread.currentThread().getName()+"主线程运行结束!");    }}

执行结果主会先结束:
这里写图片描述

2.2.sleep和yield

  • 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()方法来控制并发线程执行

2.2.1.sleep

package com.thread;/** * 描述:继承thread类 * 作者:袁伟倩 * 创建日期:2017-06-02 20:35. */public class ThreadTest extends Thread {    // 构造有参构造函数    public ThreadTest(String name){        super(name);    }    @Override    public void run() {        System.out.println(Thread.currentThread().getName() + " 线程运行开始!");        for (int i = 0; i < 100; i++) {            System.out.println(Thread.currentThread().getName() + " " + i);            if(i == 40){                // 当i为40时,让当前执行线程休眠1s                System.out.println(Thread.currentThread().getName() + " sleep 2 s 开始");                try {                    Thread.currentThread().sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(Thread.currentThread().getName() + " sleep 2 s 结束");            }        }        System.out.println(Thread.currentThread().getName() + " 线程运行结束!");    }    public static void main(String[] args) throws InterruptedException {        System.out.println(Thread.currentThread().getName()+"主线程运行开始!");        ThreadTest threadTesta = new ThreadTest("子线程a");        ThreadTest threadTestb = new ThreadTest("子线程b");        threadTesta.start();        threadTestb.start();        System.out.println(Thread.currentThread().getName()+"主线程运行结束!");    }}

运行结果:

这里写图片描述

分析结果:
执行sleep()的线程在指定的时间内肯定不会被执行,当线程a开始休眠时,是不会被执行的,所以在开始-结束的这2s钟只有线程b在执行。

2.2.2.yield

package com.thread;/** * 描述:继承thread类 * 作者:袁伟倩 * 创建日期:2017-06-02 20:35. */public class ThreadTest extends Thread {    // 构造有参构造函数    public ThreadTest(String name){        super(name);    }    @Override    public void run() {        System.out.println(Thread.currentThread().getName() + " 线程运行开始!");        for (int i = 0; i < 100; i++) {            System.out.println(Thread.currentThread().getName() + " " + i);            if(i == 40){                // 当i为40时,yield 是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会//                this.yield();                Thread.currentThread().yield();                System.out.println(Thread.currentThread().getName() + " 线程yield!");            }        }        System.out.println(Thread.currentThread().getName() + " 线程运行结束!");    }    public static void main(String[] args) throws InterruptedException {        System.out.println(Thread.currentThread().getName()+"主线程运行开始!");        ThreadTest threadTesta = new ThreadTest("子线程a");        ThreadTest threadTestb = new ThreadTest("子线程b");        threadTesta.start();        threadTestb.start();        System.out.println(Thread.currentThread().getName()+"主线程运行结束!");    }}

运行结果:
这里写图片描述

分析:一开始以为yield和sleep一样,当线程b执行到yield方法时,下面就会出现线程a的执行方法,可执行结果却还是线程b,原因:线程执行yield并不会阻塞,会进入到就绪状态,其还是会和同时运行的级别等于和大于其级别的一同运行。所以运行结果就讲通了。

2.3.interrupt()

  • interrupt:置线程的中断状态

不要以为它是中断某个线程!它只是线线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出抛出,从而结束线程,但是如果你吃掉了这个异常,那么这个线程还是不会中断的!

  • isInterrupt:线程是否中断
  • interrupted:返回线程的上次的中断状态,并清除中断状态
    一般来说,阻塞函数,如:Thread.sleep、Thread.join、Object.wait等在检查到线程的中断状态时,会抛出InterruptedException,同时会清除线程的中断状态

对于InterruptedException的处理,可以有两种情况:
(1)外层代码可以处理这个异常,直接抛出这个异常即可
(2)如果不能抛出这个异常,比如在run()方法内,因为在得到这个异常的同时,线程的中断状态已经被清除了,需要保留线程的中断状态,则需要调用Thread.currentThread().interrupt()

另外,Thread.interrupted()在jdk库的源代码中比较常用,因为它既可以得到上一次线程的中断标志值,又可以同时清除线程的中断标志,一举两得,但同时也有坏处,就是这个函数有清除中断状态的副作用,不容易理解。

从上面的分析可以查出,通过中断机制也可以实现线程的终止,但是这种方式也是基于抛出异常的,所以这种方式也是不安全的,同时我们也可以这样理解就是中断机制就是来结束那些阻塞的线程的,因为阻塞线程回去检查中断标志位,又中断就抛异常来结束线程的运行。

2.4.wait

Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){…}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制

sleep()和wait()方法的最大区别是:

 sleep()睡眠时,保持对象锁,仍然占有该锁;
 而wait()睡眠时,释放对象锁。
 但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException(但不建议使用该方法)。
 

package com.thread;/** * 描述:继承thread类 * 作者:袁伟倩 * 创建日期:2017-06-02 20:35. */public class ThreadTest extends Thread {    // 构造有参构造函数    public ThreadTest(String name){        super(name);    }    @Override    public void run() {        synchronized (this) {            System.out.println(Thread.currentThread().getName() + " 线程运行开始!");            for (int i = 0; i < 100; i++) {                System.out.println(Thread.currentThread().getName() + " " + i);                if (i == 40) {                    // 当i为40时,让当前执行线程休眠1s                    System.out.println(Thread.currentThread().getName() + " wait 2 s 开始");                    try {                        Thread.currentThread().wait(2000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName() + " wait 2 s 结束");                }            }            System.out.println(Thread.currentThread().getName() + " 线程运行结束!");        }    }    public static void main(String[] args) throws InterruptedException {        System.out.println(Thread.currentThread().getName()+"主线程运行开始!");        ThreadTest threadTesta = new ThreadTest("子线程a");        ThreadTest threadTestb = new ThreadTest("子线程b");        threadTesta.start();        threadTestb.start();        System.out.println(Thread.currentThread().getName()+"主线程运行结束!");    }}

运行结果
这里写图片描述

注:在采用wait时,需要将代码放入synchronized中

原创粉丝点击