多线程-Thread类的常用方法和生命周期

Thread类的常用结构

构造器

  • public Thread():分配一个新的线程对象。
  • public Thread(String name):分配一个指定名字的新的线程对象。
  • public Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run()方法。
  • public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。

常用方法系列1

  • public void run():此线程要执行的任务在此处定义代码。
  • public void start():导致此线程开始执行;Java虚拟机调用此线程的run方法。
  • public String getName():获取当前线程名称。
  • public void setName():设置该线程名称。
  • public static Thread currentThread():返回对当前正在执行的线程对象的引用。在Thread子类中就是this,通常用于主线程和Runnable实现类。
  • public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
  • public static void yield(): yield只是让当前线程暂停一下,让系统的线程调度器重新调度一次,希望优先级与当前线程相同或更高的其他线程能够获得执行机会,但是这个不能保证,完全有可能的情况是,当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行。

常用方法系列2

  • public final boolean isAlive(): 测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。

  • void join(): 等待该线程终止。

    void join(long millis): 等待该线程终止的时间最长为millis毫秒。如果millis时间到,将不再等待。

    void join(long millis,int nanos): 等待该线程终止的时间最长为millis毫秒 + nanos 纳秒。

  • public final void stop(): 已过时,不建议使用。强行结束一个线程的执行,直接进入死亡状态。run()即刻停止,可能会导致一些请理性的工作得不到完成,如文件,数据库等的关闭。同时,会立即释放该线程所持有的所有的锁,导致数据得不到同步的处理,出现数据不一致的问题。

  • void suspend() / void resume(): 这两个操作就好比播放器的暂停和恢复。二者必须成对出现,否则非常容易发生死锁。suspend()调用会导致线程暂停,但不会释放任何锁资源,导致其他线程都无法访问被它占用的锁,直到调用resume()。已过时,不建议使用。

常用的方法系列3

每个线程都有一定的优先级,同优先级线程组成先进先出队列(先到先服务),使用分时调度策略。优先级高的线程采用抢占式策略,获得较多的执行机会。每个线程默认的优先级都与创建它的父线程具有相同的优先级。

  • Thread类的三个优先级常量:
    • MAX_PRIORITY(10):最高优先级
    • MIN_PRIORITY(1): 最低优先级
    • NORM_PRIORITY(5):普通优先级,默认情况下main线程具有普通优先级。
  • public final int getPriority(): 返回线程优先级
  • public final void setPriority(int newPriority): 改变线程的优先级,范围在[1——10]之间。

练习:获取main线程对象的名称和优先级。

声明一个匿名内部类继承Thread类,重写run方法,在run方法中获取线程名称和优先级。设置该线程优先级为最高优先级并启动该线程。

一、线程的常用结构
1. 线程中的构造器
- public Thread():分配一个新的线程对象。
- public Thread(String name):分配一个指定名字的新的线程对象。
- public Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run()方法。
- public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。


2.线程中的常用方法:
> start():1)启动线程 (2)调用线程的run()
> run(): 将线程要执行的操作,声明在run()中。
> currentThread(): 获取当前执行代码对应的线程。
> getName(): 获取线程名
> setName(): 设置线程名
> sleep(long millis): 静态方法,调用时,可以使得当前线程睡眠指定的毫秒数
> yield(): 静态方法,一旦执行此方法,就释放CPU的执行权
> join(): 在线程a中通过线程b调用join(),意味着线程a进入阻塞状态,直到线程b执行结束,线程a才结束阻塞状态,继续执行
> isAlive(): 判断当前线程是否还存活


过时方法:
> stop(): 强行结束一个线程的执行,直接进入死亡状态。不建议使用
> void suspend() / void resume() : 可能造成死锁,所以也不建议使用



3.线程的优先级:
getPriority(): 获取线程的优先级
setPriority(): 设置线程的优先级。范围是[1,10]

Thread类内部声明的三个常量:
- MAX_PRIORITY(10):最高优先级
- MIN_PRIORITY(1): 最低优先级
- NORM_PRIORITY(5):普通优先级,默认情况下main线程具有普通优先级。


二、线程的生命周期

常用方法练习:

package thread.demo02;

public class EvenNumberTest {
    public static void main(String[] args) {
        //测试1
        PrintNumber t1 = new PrintNumber("线程1:偶数");
        t1.setName("线程1(修改):偶数");//修改上面的线程名"线程1:偶数"为"线程1(修改):偶数"
        t1.setPriority(Thread.MAX_PRIORITY);//注意不是哪个优先级高就先运行完哪个线程,是CPU几率更高优先来调用优先级高的线程。
        // 打印出来发现还是主线程第一个运行,因为现在都是多核CPU,这是概率问题。
        t1.start();

        //测试setName()
        Thread.currentThread().setName("主线程");
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + " 优先级为:" +
                        Thread.currentThread().getPriority() + ": " + i);
            }

//            if (i == 20){
//                try {
//                    t1.join();
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }

        }

        //测试isAlive()
//        System.out.println("线程1(修改):偶数 是否存活?" + t1.isAlive());

        //测试2
        PrintNumber1 p = new PrintNumber1();
        Thread t2 = new Thread(p,"线程2:奇数");
        t2.start();

    }
}

//测试1:-public Thread(String name):分配一个指定名字的新的线程对象。
class PrintNumber extends Thread{

    //无参构造
    public PrintNumber(){

    }
    //分配一个指定名字的新的线程对象。
    public PrintNumber(String name){
        super(name);
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {

            //测试sleep()
//            try {
//                Thread.sleep(1000);//设置了1000毫秒也就是一秒,对应的这个线程1一秒钟执行一下,直到结束
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }

            if (i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + " 优先级为:" +
                        Thread.currentThread().getPriority() + ": " + i);
            }

            //测试yield()
//            if (i % 20 == 0){
//                Thread.yield();
//            }

        }
    }
}

//测试2:- public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
class PrintNumber1 implements Runnable{

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + " 优先级为: " +
                        Thread.currentThread().getPriority() + " " + i);
            }
        }
    }
}

多线程的生命周期

Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下一些状态:

JDK1.5之前:5种状态

线程的生命周期有五种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Dead)。CPU需要在多条线程之间切换,于是线程状态会多次在运行、阻塞、就绪之间切换。

多线程-Thread类的常用方法和生命周期_第1张图片

1.新建

当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。此时它和其他Java对象一样,仅仅由JVM为其分配了内存,并初始化了实例变量的值。此时的线程对象并没有任何线程的动态特征,程序也不会执行它的线程体run()。

2.就绪

但是当线程对象调用了start()方法之后,就不一样了,线程就从新建状态转为就绪状态。JVM会为其创建方法调用栈和程序计数器,当然,处于这个状态中的线程并没有开始运行,只是表示已具备了运行的条件,随时可以被调度。至于什么时候被调度,取决于JVM里线程调度器的调度。

注意:
程序只能对新建状态的线程调用start(),并且只能调用一次,如果对非新建状态的线程,如已启动的线程或已死亡的线程调用start()都会报错IllegalThreadStateException异常。

3.运行

如果处于就绪状态的线程获得了CPU资源时,开始执行run()方法的线程体代码,则该线程处于运行状态。如果计算机只有一个CPU核心,在任何时刻只有一个线程处于运行状态,如果计算机有多个核心,将会有多个线程并行(Parallel)执行。

4.阻塞

当在运行过程中的线程遇到如下情况时,会让出CPU并临时中止自己的执行,进入阻塞状态:

  • 线程调用了sleep()方法,主动放弃所占用的CPU资源;
  • 线程试图获取一个同步监视器,但该同步监视器正被其他线程持有;
  • 线程执行过程中,同步监视器调用了wait(),让它等待某个通知(notify);
  • 线程执行过程中,同步监视器调用了wait(time);
  • 线程执行过程中,遇到了其他线程对象的加塞(join);
  • 线程被调用suspend方法被挂起(已过时,因为容易发生死锁);

当前正在执行的线程被阻塞后,其他线程就有机会执行了。针对如上情况,当发生如下情况时会解除阻塞,让该线程重新进入阻塞状态,等待线程调度器再次调度它:

  • 线程的sleep()时间到;
  • 线程成功获得了同步监视器;
  • 线程等到了通知(notify);
  • 线程wait的时间到了
  • 加塞的线程结束了;
  • 被挂起的线程又被调用了resume恢复方法(已过时,因为容易发生死锁);

5.死亡

线程会以以下三种方式之一结束,结束后的线程就处于死亡状态:

  • run()方法执行完成,线程正常结束
  • 线程执行过程中抛出了一个未捕获的异常(Exception)或错误(Error)
  • 直接调用该线程的stop()来结束该线程(已过时)

JDK1.5及之后:6种状态

在java.lang.Thread.State的枚举类中这样定义:

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

下图中在JAVA基础阶段暂时了解一下,不去看里面具体方法,以后在JUC里面再看

多线程-Thread类的常用方法和生命周期_第2张图片

你可能感兴趣的:(java学习,java)