线程运行状态

不管是多进程还是多线程实质上都不太可能一直进行 CPU 资源的占用,那么针对多线程的几种状态要掌握。

 

    所有的系统资源是有限的,不管是多线程还是多进程,都必须在执行一段时间之后,让出资源,交由其他的线程继续执行,这是并发编程的本质。

1、创建过程:开发者定义好了相应的线程类对象,每一个 Thread 类的实例都表示一个线程对象;

2、就绪状态:多线程的启动依靠 start() 方法,当调用start()方法后所有的线程并不是立即执行,而是将进入到一个等待状态,等待 CPU 进行调度;

3、执行状态:当 CPU 执行调度到某个线程对象之后,该线程对象开始执行 run() 方法或 call() 但是并不意味一执行就将持续占用资源,而是在一段时间之后(一个时间片的时间之后),该线程就暂停执行;

4、阻塞状态:当某一个线程不在执行时(中断、休眠或调度失效),那么所有的线程将进入阻塞状态,如果此时线程没有执行完毕,则由阻塞状态切换到就绪状态,重新等待 CPU 的执行调度;

5、终止状态:如果多线程的执行代码体执行完毕或线程被强制性的结束,那么就将进入终止状态,将不会在进入就绪状态,即:该线程对象将不会被继续执行。

 

 

Thread类控制多线程:

       通过之前分析应该对于多线程的类和接口有一个核心的认识,Runnable 和 Callable 主要定义线程的核心内容,而 Thread实现线程的控制,所以Thread 类中除了有启动多线程的方法外还有若干个控制操作。

线程命名与获取

多线程的运行状态是不固定的,所以针对所有的线程对象而言,就只能够通过名称来进行线程的唯一标记。供有如下的几个方法可以实现线程名称的操作。

NO 方法名称 类型 描述
1 public Thread(Runnable target, String name) 构造 接受Runnable及线程的名字
2 public final void setName(String name) 方法 设置 & 修改名字
3 public final void getName(String name) 方法 获取线程名字

 

由于所有的线程状态都是不可控的,所以唯一可以获取的只能时当前地线程对象,此时可以使用Thread 类中提供的线程方法:

public static Thread currentThread()

范例:观察线程的命名与获取:

import java.util.concurrent.Callable;

class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程运行。。。。。");
    }
}
public class MyBlog3 {
    public static void main(String[] args) {
    MyThread threadBody = new MyThread();
    new Thread(threadBody).start();
    new Thread(threadBody,"运行线程A").start();
    new Thread(threadBody).start();
    new Thread(threadBody,"运行线程B").start();
    }
}
运行结果:

运行线程B线程运行。。。。。
Thread-1线程运行。。。。。
Thread-0线程运行。。。。。
运行线程A线程运行。。。。。

 

可以发现,此时即使没有为线程设置名称,线程也会自动的生成一个名称。此名称实际上是采用线程创建顺序自动进行编号处理。

public Thread(ThreadGroup group, Runnable target)分配一个新的Thread对象。 此构造具有相同的效果Thread (group, target, gname) 
,其中gname是新生成的名字。 自动生成的名称格式为
"Thread-"+ n ,其中n为整数。
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
       return threadInitNumber++;
}

通过源代码的分析可以发现,此时在 Thread 类中提供一个 static 的整型变量,这与之前分析的属性自动命名的结构是类似的。

观察如下代码:

import java.util.concurrent.Callable;

class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程运行。。。。。");
    }
}
public class MyBlog3 {
    public static void main(String[] args) {
    MyThread threadBody = new MyThread();
    new Thread(threadBody,"自定义的的线程对象").start();
    threadBody.run();

    }
}
运行结果:
自定义的的线程对象线程运行。。。。。 main线程运行。。。。。

      其实所有的主方法的执行都是在 Java 程序里以主线程(main)的形式运行,所有的线程都是由进程创建的,那么进程在哪里?

每当用户使用 java 命令解释一个类的时候,实际就会自动启动一个 JVM 的进程。

范例:观察进程的存在:

public class Demo{
      public static void main(String args[]){
            String str = "";
            for ( int x = 0; x < Integer.MAX_VALUE ; x++ ){
                  str  += x ;                     
              }
        }
}

 

所有的JVM 程序运行的时候都会默认启动一个新的进程,而现在的所有线程都是在此进程中产生的,在该进程执行的时候,会默认启动一个主线程,由主线程创建若干个子线程,所有的子线程并行执行。

 

 

线程休眠:

默认情况下线程对象只要启动了,那么就会持续的运行,一直到其运行完毕为止,但如果现在希望线程的执行速度可以缓慢一些,那么就可以对其进行休眠处理,在 Thread 类里提供了休眠的方法:

休眠:
public static void sleep(long millis) throws InterruptedException

范例:观察休眠:

import java.util.concurrent.Callable;

class MyThread implements Runnable{
    @Override
    public void run() {
        for (int x = 0; x <20 ; x++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread
            .currentThread().getName()+"运行,x="+x);
        }
    }
}
public class MyBlog3 {
    public static void main(String[] args) {
    MyThread Body = new MyThread();
    new Thread(Body,"运行线程").start();
    Body.run();
    }
}

此时进入run()方法的线程对象可能有多个,但是会发现这多个线程对象感觉像一起休眠,一起自动唤醒。

未做任何处理的时候,所有的线程将同时进入run()方法之中,run()的代码也会按照先后的顺序依次执行,但由于多个线程之间的执行速度不一样,所以感觉像是同时执行。

线程中断

       在之前使用休眠的时候会发现抛出一个"InterruptException",实际上就表示中断异常,也就是说所有的线程的执行都是可以被中断的。在Thread 类中提供有中断处理方法如下:

  • public void interrupt() //中断线程。
  • public boolean isInterrupted() //测试线程是否已经中断。线程的中断状态 不受该方法的影响。

范例:

public class MyBlog3 {
    public static void main(String[] args)throws Exception {
        Thread thread = new Thread(()->{
            try {
                Thread.sleep(6000);
                System.out.println("吃饱喝足,开始学习");
            } catch (InterruptedException e) {
                System.out.println("休息被中断了,开始玩");
            }
        });
        thread.start();
        System.out.println(thread.getName()+"线程的中断状态:"+thread.isInterrupted());
        Thread.sleep(1000);  //先执行1秒
        thread.interrupt();      //由主线程中断一个子线程
        System.out.println(thread.getName()+"线程的中断状态:"+thread.isInterrupted());
    }
}
运行结果:
Thread-0线程的中断状态:false
休息被中断了,开始玩
Thread-0线程的中断状态:true

线程中断的本质是:一个线程被另一个线程打断执行。线程一旦被中断,就会引发线程中断异常。

 

线程的强制执行

     当程序中存在若干个线程的时候,那么这若干个线程之间彼此肯定是相互交替执行的,但若此时某个线程很紧急,需要优先处理完成,就可以采用线程的强制执行;

 

public final void join() throws InterruptedException          //等待该线程终止。
public final void join(long millis) throws InterruptedException
//等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去
public final void join(long millis, int nanos)throws InterruptedException
//等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。

范例:观察线程的强制执行:

public class MyBlog3 {
    public static void main(String[] args) throws Exception {
        Thread mainThread = Thread.currentThread();
        Thread thread = new Thread(() -> {
            for (int x = 0; x < 1000; x++) {
                if (x == 10) {
                    try {
                        mainThread.join();   //在第十个的时候主线程开始强制执行;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "执行,x = " + x);
            }
        }, "循环线程");
        thread.start();
        for (int x = 0; x < 1000; x++) {
            Thread.sleep(1000);  //延缓程序的执行速度
            System.out.println(Thread.currentThread().getName() + "执行,x = " + x);
        }
    }
}

 

执行结果(截选有代表性的一部分):
..................................
循环线程执行,x = 8
循环线程执行,x = 9
main执行,x = 0
main执行,x = 1
main执行,x = 2
main执行,x = 3
main执行,x = 4
main执行,x = 5
...................................

   使用 join() 会将当前的线程资源让出,交由其他线程执行,如果没有设置任何的时间单位,那么将等待其他线程执行完毕再恢复剩余线程的正常执行。

 

线程礼让

     礼让的概念就是让出当前的执行操作,例如男生礼让女士座位。在两个线程进行资源处理的时候也可以实现这种谦让操作;

public static void yield()   //暂停当前正在执行的线程对象,并执行其他线程。

范例:观察线程的礼让操作:public class MyBlog3 {

    public static void main(String[] args) throws Exception {
Thread mainThread = Thread.currentThread();
Thread thread = new Thread(() -> {
for (int x = 0; x < 1000; x++) {
if (x % 3 == 0) {
Thread.yield(); // 礼让一次
System.out.println("【YIELD】线程礼让," + Thread.currentThread().getName());
}
try {
Thread.sleep(1000); //休眠一秒的时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行,x = " + x);
}
}, "循环线程");
thread.start();
for (int x = 0; x < 1000; x++) {
Thread.sleep(1000); //延缓程序的执行速度
System.out.println(Thread.currentThread().getName() + "执行,x = " + x);
}
}

}

执行结果(截取):
【YIELD】线程礼让,循环线程
main执行,x = 0
循环线程执行,x = 0
main执行,x = 1
循环线程执行,x = 1
循环线程执行,x = 2
main执行,x = 2
【YIELD】线程礼让,循环线程
循环线程执行,x = 3
main执行,x = 3
main执行,x = 4
循环线程执行,x = 4
main执行,x = 5
循环线程执行,x = 5
【YIELD】线程礼让,循环线程
main执行,x = 6
循环线程执行,x = 6
循环线程执行,x = 7
main执行,x = 7
main执行,x = 8
循环线程执行,x = 8
【YIELD】线程礼让,循环线程
循环线程执行,x = 9
main执行,x = 9
main执行,x = 10
循环线程执行,x = 10
main执行,x = 11
循环线程执行,x = 11
【YIELD】线程礼让,循环线程
main执行,x = 12
循环线程执行,x = 12

礼让指表示礼让一次,,随后就会继续按照资源抢占的模式来进行多线程的并发处理。

 

线程的优先级

      理论上线程的优先级越高,越有可能先执行,在Thread类中为了方便优先级的管理定义有如下的优先级的操作方法:

  NO 方法及常量 类型  
1
public final void setPriority(int newPriority)
方法 设置线程的优先级
2
public final int getPriority
方法 获取优先级
3
public static final int MIN_PRIORITY 
常量 最低优先级(数值为10)
4
public static final int NORM_PRIORITY 
常量 中等优先级(5)
5
public static final int MAX_PRIORITY 
常量 最高优先级(1)

范例:线程优先级的设置:

public  class MyBlog3{
    public static void main(String[] args) throws Exception {
        Runnable run = ()->{
            for (int x = 0; x < 100 ; x++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"运行,x = "+x);
            }
        };
        Thread threadA = new Thread(run,"线程A");
        Thread threadB = new Thread(run,"线程B");
        Thread threadC = new Thread(run,"线程C");
        threadA.setPriority(Thread.MIN_PRIORITY);
        threadB.setPriority(Thread.NORM_PRIORITY);
        threadC.setPriority(Thread.MAX_PRIORITY);
        threadA.start();
        threadB.start();
        threadC.start();
    }
}
运行结果(截选):
线程C运行,x = 0
线程B运行,x = 0
线程A运行,x = 0
线程C运行,x = 1
线程B运行,x = 1
线程A运行,x = 1
线程C运行,x = 2
线程B运行,x = 2
线程A运行,x = 2
线程A运行,x = 3
线程B运行,x = 3

 


优先级越高越有可能先进行调度,但是观察后发现并不是绝对的,同时也可以观察一个方法:

范例:主线程的优先级

public  class MyBlog3{
    public static void main(String[] args) throws Exception {
       System.out.println(Thread.currentThread().getPriority());
       }
}
运行结果:
5

通过结果可以发现用户自己创建的线程和主线程的优先级是相同的。

 

等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去

posted on 2020-01-06 16:52  Creepooo  阅读(365)  评论(0)    收藏  举报