java获取系统线程数 java线程_就绪状态

线程对象是可以产生线程的对象。比如在Java平台中Thread对象,Runnable对象。线程,是指正在执行的一个指点令序列。在java平台上是指从一个线程对象的start()开始,运行run方法体中的那一段相对独立的过程。相比于多进程,多线程的优势有:

 

 

    (1)进程之间不能共享数据,线程可以;

 

    (2)系统创建进程需要为该进程重新分配系统资源,故创建线程代价比较小;

 

    (3)Java语言内置了多线程功能支持,简化了java多线程编程。

 

一、创建线程和启动

 

  (1)继承Thread类创建线程类

 

通过继承Thread类创建线程类的具体步骤和具体代码如下:

 

   • 定义一个继承Thread类的子类,并重写该类的run()方法;

 

   • 创建Thread子类的实例,即创建了线程对象;

 

   • 调用该线程对象的start()方法启动线程。

 

复制代码

class SomeThead extends Thraad { 
    public void run() { 
     //do something here  
    }  
 } 
 
public static void main(String[] args){
 SomeThread oneThread = new SomeThread();

  步骤3:启动线程:   

 oneThread.start(); 

}

复制代码

    (2)实现Runnable接口创建线程类

 

通过实现Runnable接口创建线程类的具体步骤和具体代码如下:

 

   • 定义Runnable接口的实现类,并重写该接口的run()方法;

 

   • 创建Runnable实现类的实例,并以此实例作为Thread的target对象,即该Thread对象才是真正的线程对象。

 

复制代码

class SomeRunnable implements Runnable { 
  public void run() { 
  //do something here  
  }  
} 
Runnable oneRunnable = new SomeRunnable();   
Thread oneThread = new Thread(oneRunnable);   
oneThread.start();

复制代码

    (3)通过Callable和Future创建线程

 

通过Callable和Future创建线程的具体步骤和具体代码如下:

 

   • 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

   • 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

   • 使用FutureTask对象作为Thread对象的target创建并启动新线程。

   • 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值其中,Callable接口(也只有一个方法)定义如下: 

 

复制代码

public interface Callable { 
  V call() throws Exception;  
 }

  步骤1:创建实现Callable接口的类SomeCallable(略);   

  步骤2:创建一个类对象: 

      Callable oneCallable = new SomeCallable(); 

  步骤3:由Callable创建一个FutureTask对象:   

    FutureTask oneTask = new FutureTask(oneCallable); 

  注释: FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了 Future和Runnable接口。 

  步骤4:由FutureTask创建一个Thread对象:   

    Thread oneThread = new Thread(oneTask);   

  步骤5:启动线程:  

    oneThread.start(); 

复制代码

二、线程的生命周期

 

                        

 

1、新建状态

 

       用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable)。

 

注意:不能对已经启动的线程再次调用start()方法,否则会出现Java.lang.IllegalThreadStateException异常。

 

2、就绪状态

 

       处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列(尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。因为cpu的调度不一定是按照先进先出的顺序来调度的),等待系统为其分配CPU。等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。

 

提示:如果希望子线程调用start()方法后立即执行,可以使用Thread.sleep()方式使主线程睡眠一伙儿,转去执行子线程。

 

3、运行状态

 

      处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

 

处于就绪状态的线程,如果获得了cpu的调度,就会从就绪状态变为运行状态,执行run()方法中的任务。如果该线程失去了cpu资源,就会又从运行状态变为就绪状态。重新等待系统分配资源。也可以对在运行状态的线程调用yield()方法,它就会让出cpu资源,再次变为就绪状态。

 

注: 当发生如下情况是,线程会从运行状态变为阻塞状态:

 

     ①、线程调用sleep方法主动放弃所占用的系统资源

 

     ②、线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞

 

     ③、线程试图获得一个同步监视器,但更改同步监视器正被其他线程所持有

 

     ④、线程在等待某个通知(notify)

 

     ⑤、程序调用了线程的suspend方法将线程挂起。不过该方法容易导致死锁,所以程序应该尽量避免使用该方法。

 

当线程的run()方法执行完,或者被强制性地终止,例如出现异常,或者调用了stop()、desyory()方法等等,就会从运行状态转变为死亡状态。

4、阻塞状态

 

      处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。 

 

在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。有三种方法可以暂停Threads执行:

 

5、死亡状态

 

      当线程的run()方法执行完,或者被强制性地终止,就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

 

三、线程管理

 

     Java提供了一些便捷的方法用于会线程状态的控制。具体如下:

 

1、线程睡眠——sleep

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

 

注:

 

   (1)sleep是静态方法,最好不要用Thread的实例对象调用它,因为它睡眠的始终是当前正在运行的线程,而不是调用它的线程对象,它只对正在运行状态的线程对象有效。如下面的例子:

 

复制代码

public class Test1 {  
    public static void main(String[] args) throws InterruptedException {  
        System.out.println(Thread.currentThread().getName());  
        MyThread myThread=new MyThread();  
        myThread.start();  
        myThread.sleep(1000);//这里sleep的就是main线程,而非myThread线程  
        Thread.sleep(10);  
        for(int i=0;i<100;i++){  
            System.out.println("main"+i);  
        }  
    }  
}

复制代码

     (2)Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率。但是不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。因为使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。

 

2、线程让步——yield

 

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

 

实际上,当某个线程调用了yield()方法暂停之后,优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程更有可能获得执行的机会,当然,只是有可能,因为我们不可能精确的干涉cpu调度线程。用法如下:

 

复制代码

public class Test1 {  
    public static void main(String[] args) throws InterruptedException {  
        new MyThread("低级", 1).start();  
        new MyThread("中级", 5).start();  
        new MyThread("高级", 10).start();  
    }  
}  
  
class MyThread extends Thread {  
    public MyThread(String name, int pro) {  
        super(name);// 设置线程的名称  
        this.setPriority(pro);// 设置优先级  
    }  
  
    @Override  
    public void run() {  
        for (int i = 0; i < 30; i++) {  
            System.out.println(this.getName() + "线程第" + i + "次执行!");  
            if (i % 5 == 0)