`
神绮_H_亚里亚
  • 浏览: 10439 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

线程(中)

阅读更多

   线程的整个周期由线程创建、可运行状态、不可运行状态和退出等部分组成,这些状态之间的转化是通过线程提供的一些方法完成的。

1线程周期

 

      一个线程有4 种状态,任何一个线程都处于这4种状态中的一种状态
     
 1) 创建(new)状态:调用 new方法产生一个线程对象后、调用 start 方法前所处的状态。线程对象虽然已经创建,但还没有调用 start 方法启动,因此无法执行。当线程处于创建状态时,线程对象可以调用 start 方法进入启动状态,也可以调用 stop 方法进入停止状态。 
     
2)可运行(runnable)状态:当线程对象执行 start()方法后,线程就转到可运行状态。进入此状态只是说明线程对象具有了可以运行的条件,但线程并不一定处于运行状态。因为在单处理器系统中运行多线程程序时,一个时间点只有一个线程运行,系统通过调度机制实现宏观意义上的运行线程共享处理器。因此一个线程是否在运行,除了线程必须处于 Runnable 状态之外,还取决于优先级和调度。 
    
3)不可运行(non Runnable)状态:线程处于不可运行状态是由于线程被挂起或者发生阻塞,例如对一个线程调用 wait()函数后,它就可能进入阻塞状态;调用线程的notifynotifyAll 方法后它才能再次回到可执行状态。 
     
4)退出(done)状态:一个线程可以从任何一个状态中调用 stop 方法进入退出状态。线程一旦进入退出状态就不存在了,不能再返回到其他的状态。除此之外,如果线程执行完 run方法,也会自动进入退出状态。 
     
创建状态、可运行状态、不可运行状态、退出状态之间的转换关系如图所示。 


 通过 new第一次创建线程时,线程位于创建状态,这时不能运行线程,只能等待进一步的方法调用改变其状态。然后,线程通过调用 start方法启动
线程,并进入可执行状态,或者调用方法 stop进入退出状态。当程序位于退出状态时,线程已经结束执行,这是线程的最后一个状态,并且不能转化到其他状态。当程序的所有线程位于退出状态时,程序会强行终止。当线程位于可执行状态时,在一个特定的时间点上,每一个系统处理器只能运行一个线程。此时如果线程被挂起,执行就会被中断或者进入休眠状态,那么线程将进入不可执行状态,并且不可执行状态可以通过 resumenotify等方法返回到可执行状态。表10-1列举了线程状态转换的函数。 

     
  线程状态转换函数
方法                        描述                                                          有效状态             目的状态 
start()                    
开始执行一个线程                                     New                    Runnable 
stop()                    
结束执行一个线程                                     NewRunnable    Done 
sleep(long)          
暂停一段时间,这个时间为给定的毫秒  Runnable             NonRunnable 
sleep(long,int)    
暂停片刻,可以精确到纳秒                     Runnable             NonRunnable 
suspend()            
挂起执行                                                      Runnable             NonRunnable 
resume()              
恢复执行                                                      NonRunnable        Runnable 
yield()                    
明确放弃执行                                              Runnable             Runnable 
wait()                    
进入阻塞状态                                              Runnable             NonRunnable 
notify()                  
阻塞状态解除                                              NonRunnable       Runnable 
注意:stop()suspend() resume()方法现在已经不提倡使用,这些方法在虚拟机中可能引起死锁现象。suspend() resume()方法的替代方法是 wait() sleep()。线程的退出通常采用自然终止的方法,建议不要人工调用 stop()方法。 

2 线程的创建和启动 

      Java 是面向对象的程序设计语言,设计的重点就是类的设计与实现。Java 利用线程类Thread 来创建线程,线程的创建与普通类对象的创建操作相同。Java通过线程类的构造方法创建一个线程,并通过调用 start 方法启动该线程。 
       
实际上,启动线程的目的就是为了执行它的 run()方法,而 Thread 类中默认的 run()方法没有任何可操作代码,所以用 Thread类创建的线程不能完成任何任务。为了让创建的线程完成特定的任务,必须重新定义 run()方法。在第一节中已经讲述过,Java 通常有两种重新定义run()方法的方式:

      1)派生线程类 Thread 的子类,并在子类中重写 run()方法:Thread 子类的实例对象是一个线程对象,并且该线程有专门定制的线程 run()方法,启动线程后就执行子类中重写的 run()方法。 
‰    
  2)实现 Runnable 接口并重新定义 run()方法:先定义一个实现 Runnable()接口的类,在该类中定义 run()方法,然后创建新的线程类对象,并以该对象作为 Thread 类构造方法的参数创建一个线程。 
      
     
注意:调用线程的 run()方法是通过启动线程的start()方法来实现的。因为线程在调用start()方法之后,系统会自动调用 run()方法。与一般方法调用不同的地方在于一般方法调用另外一个方法后,必须等被调用的方法执行完毕才能返回,而线程的 start()方法被调用之后,系统会得知线程准备完毕并且可以执行run()方法,start()方法就返回了,start()方法不会等待run()方法执行完毕。
 

线程状态转换 

 

1.线程进入可执行状态 
   
当以下几种情况发生时,线程进入可执行状态。 
1)其他线程调用notify()或者 notifyAll()方法,唤起处于不可执行状态的线程。 
      public final void notify() 
      public final void notifyAll() 
     notify
仅仅唤醒一个线程并允许它获得锁,notifyAll 唤醒所有等待这个对象的线程,并允许它们获得锁。
2)线程调用 sleep(millis)方法,millis毫秒之后线程会进入可执行状态。 
         static void sleep(long millis) throws InterruptedException 
millis 毫秒数内让当前正在执行的线程进入休眠状态,等到时间过后,该线程会自动苏醒并继续执行。sleep方法的精确度受到系统计数器的影响。 
        static void sleep(long millis, int nanos) throws InterruptedException 
在毫秒数(millis)加纳秒数(nanos)内让当前正在执行的线程进入休眠状态,此操作的精确度也受到系统计数器的影响。 

 

3)线程对I/O操作的完成。 

 

2.线程进入不可执行状态 
   
当以下几种情况发生时,线程进入不可执行状态。 
1)线程自动调用 wait()方法,等待某种条件的发生。 
      public final void wait() throws InterruptedException 
     
当其他线程调用 notify()方法或 notifyAl()方法后,处于等待状态的线程获得锁之后才会被唤醒,然后该线程一直等待重新获得对象锁才继续运行。 
2)线程调用 sleep()方法进入不可执行状态,在一定时间后会进入可执行状态。 
3)线程等待 I/O操作的完成。 

 

   线程阻塞的例子。 

 

public class  ThreadSleep   
{   
 public static void main(String[ ] args)    
 {   
  SubThread st = new SubThread("SubThread");  //创建,并初始化SubThread 对象st   
  st.start();         //启动线程st   
 }   
}   
   
	class SubThread extends Thread{   
	 SubThread(){}        //声明,实现SubThread无参数构造方法   
	 //声明,实现SubThread带字符串参数构造方法   
	 SubThread(String Name)   
	 {   
	  super(Name);        //调用父类的构造方法   
	 }   
	 //重载run函数   
	 public void run()   
	 {   
	  for (int count = 1,row = 1; row < 10; row++,count++) //循环计算输出的*数目   
	  {   
	   for (int i = 0; i < count; i++)      //循环输出指定的count数目的*   
	   {   
	    System.out.print('*');     //输出*   
	   }   
	   try         //try-catch块,用于捕获异常   
	   {     Thread.sleep(1000);     //线程休眠1秒钟   
	    System.out.print("\t wait........");   
	   }   
	   catch (InterruptedException e)    //捕获异常InterruptedException   
	   {   
	    e.printStackTrace();     //异常抛出信息   
	   }   
	   System.out.println();      //输出换行符   
	  }   
	 }   
	}   

     程序ThreadSleep 中,每输出一行*就要休息1 秒钟。当执行 sleep()语句后,线程进入不可执行状态等待1 秒钟之后,线程 st 会自动苏醒并继续执行。由于 sleep

 

 

方法抛出 InterruptedException异常,所以在调用时必须捕获异常。 

 

4 等待线程结束 

 

     isAlive()方法用来判断一个线程是否存活。当线程处于可执行状态或不可执行状态时,isAlive()方法返回 true当线程处于创建状态或退出状态时,则返回 false也就是说, isAlive()方法如果返回 true,并不能判断线程是处于可运行状态还是不可运行状态。isAlive()方法的原型如下所示。 
      public final boolean isAlive() 

 

      该方法用于测试线程是否处于活动状态。活动状态是指线程已经启动(调用 start方法)且尚未退出所处的状态,包括可运行状态和不可运行状态。可以通过该方法解决程序 10.3中的问题,先判断第一个线程是否已经终止,如果终止再来调用第二个线程。这里提供两种方法:

 

      第一种方法是不断查询第一个线程是否已经终止,如果没有,则让主线程睡眠一直到它终止即“while/isAlive/sleep”,格式如下。 

 

<!--[if !supportLists]-->1.     <!--[endif]-->线程1.start();   

 

<!--[if !supportLists]-->2.     <!--[endif]-->while(线程 1.isAlive()) {   

 

<!--[if !supportLists]-->3.     <!--[endif]--> Thread.sleep(休眠时间);   

 

<!--[if !supportLists]-->4.     <!--[endif]-->}   

 

<!--[if !supportLists]-->5.     <!--[endif]-->线程2.start();   

    第二种是利用 join()方法。 
    1)public final void join(long millis) throws InterruptedException 
等待该线程终止的时间最长为毫秒(millis),超时为0 意味着要一直等下去。 
    2) public final void join(long millis,int nanos) throws InterruptedException 
等待该线程终止的时间最长为毫秒(millis)加纳秒(nanos)。  
    3)public final void join() throws InterruptedException 
等待该线程终止。 
   
等待线程结束并执行另外一个线程的例子。   该例子 等待一个线程的结束的两种方法  :

 

package Test;  
  
class WaitThreadStop extends Thread {  
    // 声明,并实现WaitThreadStop无参数构造方法  
    WaitThreadStop() {  
    }  
  
    // 声明,并实现带有一个字符串参数的构造方法  
    WaitThreadStop(String szName) {  
	        super(szName); // 调用父类的构造方法  
	    }  
	  
	    // 重载run函数  
	    public void run() {  
	        for (int count = 1, row = 1; row < 10; row++, count++) {  
	            for (int i = 0; i < count; i++) {  
	                System.out.print('*'); // 输出*  
	            }  
	            System.out.println(); // 输出换行符  
	        }  
	    }  
	}  
	  
	public class WaitThreadStopMain {  
	    public static void main(String argv[ ]){   
	      WaitThreadStopMain test = new WaitThreadStopMain();    //创建,初始化WaitThreadStopMain对象test   
	      test.Method1();  //调用Method1方法   
	      //test.Method2();   
	     }  
	    // 第一种方法:while/isAlive/sleep  
	    public void Method1() {  
	        WaitThreadStop th1 = new WaitThreadStop(); // 创建,并初始化WaitThreadStop对象th1  
	        WaitThreadStop th2 = new WaitThreadStop(); // 创建,并初始化WaitThreadStop对象th2  
	        // 执行第一个线程  
	        th1.start();  
	        // 查询第一个线程的状态  
	        while (th1.isAlive()) {  
	            try {  
	                Thread.sleep(100); // 休眠100毫秒  
	            } catch (InterruptedException e) {  
	                e.printStackTrace(); // 异常信息输出  
	            }  
	        }  
	        // 当第一个线程终止后,运行第二个线程  
	        th2.start(); // 启动线程th2  
	    }  
	  
	    // 第二种方法,使用join方法实现等待其他线程结束  
	    public void Method2() {  
	        WaitThreadStop th1 = new WaitThreadStop(); // 创建, 并初始化WaitThreadStop对象th1  
	        WaitThreadStop th2 = new WaitThreadStop(); // 创建,并初始化WaitThreadStop对象th2  
	        // 执行第一个线程  
	        th1.start();  
	        try {  
	            th1.join(); // th1调用join 方法  
	        } catch (InterruptedException e) {  
	            e.printStackTrace(); // 异常信息输出  
	        }  
	        // 执行第二个线程  
	        th2.start();  
	    }  
	}  

   多线程应用程序的每一个线程的重要性和优先级可能不同,例如有多个线程都在等待获得CPU的时间片,那么优先级高的线程就能抢占CPU并得以执行;当多个线程交替抢占CPU时,优先级高的线程占用的时间应该多。因此,高优先级的线程执行的效率会高些,执行速度也会快些。 

 

 

        Java 中,CPU的使用通常是抢占式调度模式不需要时间片分配进程。抢占式调度模式是指许多线程同时处于可运行状态,但只有一个线程正在运行。当线程一直运行直到结束,或者进入不可运行状态,或者具有更高优先级的线程变为可运行状态,它将会让出 CPU。线程与优先级相关的方法如下:

 

      public final void setPriority(int newPriority) 设置线程的优先级为 newPriority           

 

      newPriority 的值必须在 MIN_PRIORITY MAX_PRIORITY范围内,通常它们的值分别是110。目前Windows系统只支持3个级别的优

 

先级,它们分别是Thread.MAX_PRIORITY Thread.MIN_PRIORITYThread.NORM_PRIORITY  

 

      public final int getPriority() 获得当前线程的优先级。

 

     线程优先级的例子:

 

 

class  InheritThread extends Thread {   
    //自定义线程的run()方法   
    public void run(){   
         System.out.println("InheritThread is running…"); //输出字符串信息   
         for(int i=0;i<10;i++){   
              System.out.println(" InheritThread: i="+i);  //输出信息   
              try{   
                  Thread.sleep((int)Math.random()*1000); //线程休眠   
             }   
	             catch(InterruptedException e)     //捕获异常   
	             {}   
	        }   
	    }   
	}   
    //通过Runnable接口创建的另外一个线程 :
class RunnableThread implements Runnable {  
    // 自定义线程的run()方法  
    public void run() {  
        System.out.println("RunnableThread is running…"); // 输出字符串信息  
        for (int i = 0; i < 10; i++) {  
            System.out.println("RunnableThread : i=" + i); // 输出i  
            try {  
                Thread.sleep((int) Math.random() * 1000); // 线程休眠  
            } catch (InterruptedException e) { // 捕获异常  
	            }  
	        }  
	    }  
	}  
	  
	public class ThreadPriority {  
	    public static void main(String args[]) {  
	        // 用Thread类的子类创建线程  
	        InheritThread itd = new InheritThread();  
	        // 用Runnable接口类的对象创建线程  
	        Thread rtd = new Thread(new RunnableThread());  
	        itd.setPriority(5); // 设置myThread1的优先级5  
	        rtd.setPriority(5); // 设置myThread2的优先级5  
	        itd.start(); // 启动线程itd  
	        rtd.start(); // 启动线程rtd  
	    }  
	}  

 在程序ThreadPriority.java中,线程 rtd itd 具有相同的优先级,所以它们交互占用 CPU,宏观上处于并行运行状态。结果如图3. 

 

 

重新设定优先级: 

itd.setPriority(1);  //设置myThread1的优先级
rtd.setPriority(10); //
设置myThread2的优先级10 
运行程序结果如图所示。



相同优先级


不同优先级
 
 

 从运行结构可以看出程序ThreadPriority.java修改后,由于设置了线程itd rtd 的优先级,并且 rtd的优先级较高,基本上是 rtd都优先抢占 CPU资源。 



 

分享到:
评论

相关推荐

    VC++在线程中创建并显示窗口

    本篇将深入讲解如何在VC++的线程中创建并显示窗口,以及相关的技术要点。 首先,我们要理解线程的基本概念。线程是操作系统分配CPU时间的基本单元,一个进程可以有多个线程,每个线程独立执行不同的任务。在Windows...

    c++与Qt实现把定时器放进子线程中运行

    在GUI应用程序中,通常主线程负责处理用户界面的更新,而其他任务则应放在子线程中处理,以免阻塞主线程,导致UI响应变慢。标题和描述所提到的是如何将QTimer对象放置到子线程中运行,以实现定时器槽函数与QTimer在...

    在独立线程中显示WPF窗口

    在WPF(Windows Presentation Foundation)应用开发中,有时我们需要在独立的线程中显示窗口,以避免阻塞主线程,提升用户体验。标题“在独立线程中显示WPF窗口”和描述提到了这一关键概念,这涉及到多线程技术以及...

    线程中控制窗口内容

    标题"线程中控制窗口内容"主要探讨的是如何在工作线程中操作和更新MFC的对话框(Dialog)或者窗口(Window)元素。这通常涉及到线程同步和消息机制,因为主线程通常负责处理用户界面(UI)的事件,而工作线程则负责...

    线程中获取spring 注解bean

    线程中的操作往往涉及到多线程环境下的资源共享和管理,因此,如何在线程中正确地获取并使用Spring通过注解注入的对象,是一个常见的问题。本文将详细探讨这个主题。 首先,Spring的注解主要分为三类:配置注解(如...

    线程中创建子线程

    本主题主要关注如何在已有的线程中创建子线程,实现更复杂的并发执行模式。 标题中的"线程中创建子线程"是指在主线程或者其他已存在的线程内,通过编程手段创建新的子线程来执行特定的任务。这种方式通常用于执行...

    易语言线程中的变量应用

    在这个主题“易语言线程中的变量应用”中,我们将探讨如何在多线程环境下使用易语言处理变量,以及涉及的相关函数。 线程是操作系统分配CPU时间的基本单元,它允许程序同时执行多个任务。在易语言中,创建线程通常...

    C++在多线程中使用mciSendString播放音乐demoB

    本示例"C++在多线程中使用mciSendString播放音乐demoB"着重于如何在多线程环境下利用Windows Multimedia Control Interface (MCI) 函数mciSendString来实现音乐播放。下面我们将深入探讨这两个关键概念。 首先,多...

    C#线程互锁和线程中调用控件实现传值

    在C#编程中,线程互锁和线程中调用控件是多线程编程中的关键概念。本文将深入探讨这两个主题,并提供一个实际的例子来帮助理解它们的应用。 线程互锁,也称为锁或同步,是确保多个线程在访问共享资源时不会发生数据...

    【源码】[疑难]在单线程中模拟多线程的工作模式

    标题中的“【源码】[疑难]在单线程中模拟多线程的工作模式”意味着我们要探讨如何在单线程环境中模拟多线程的行为。在.NET框架中,尤其是在Windows编程中,多线程是一个常见的需求,它能提高程序的并发性能和响应...

    易语言源码将程序转换到线程中执行.rar

    在“易语言源码将程序转换到线程中执行.rar”这个压缩包中,包含的源代码是关于如何在易语言中实现程序在独立线程中运行的技术。线程是操作系统调度的基本单元,它允许一个程序内部同时执行多个任务,从而提高程序的...

    易语言正确退出线程

    在主线程或其它线程中,可以使用“发送消息”命令向目标线程发送一个特定的消息,如自定义的退出消息,然后在线程的事件处理函数中根据接收到的消息决定是否结束线程。 4. **线程回调**:设置线程的回调函数,当...

    MFC中利用多线程实现定时器

    然而,这个方法是在主线程中运行的,如果需要在后台线程中实现定时功能,我们就需要自定义一个基于多线程的定时器。 步骤如下: 1. **创建线程类**:首先,从`CWinThread`派生一个新类,例如`CMyTimerThread`,并...

    delphi多线程调用dll

    接下来,我们讨论如何在多线程中调用DLL。在Delphi中,可以使用GetProcAddress函数来获取DLL中的导出函数地址,然后通过类型转换和内存管理来调用这些函数。例如,如果DLL提供了一个名为`MyFunction`的导出函数,...

    C# 线程更新UI界面

    本篇文章将深入探讨如何通过委托在子线程中更新UI界面。 1. **多线程基础知识** - 线程:线程是程序中的执行流,每个进程至少有一个线程。 - 主线程:应用程序启动时创建的第一个线程,负责处理UI交互和事件。 -...

    C#多线程刷新界面

    1. **BackgroundWorker组件**:这是.NET Framework提供的一种简单易用的多线程解决方案,适合在UI线程中启动,并在工作线程上执行任务。BackgroundWorker提供了ProgressChanged和RunWorkerCompleted事件,可以在UI...

    C#多线程消息处理例子

    总结来说,这个C#多线程消息处理例子展示了如何在后台线程中执行任务并安全地将结果传递给UI线程。它涉及到的关键技术包括线程创建、消息队列、事件与委托、以及线程安全的UI更新。理解并掌握这些概念对于编写高性能...

    QT+OPENGL 多线程测试

    在多线程环境中,纹理拷贝指的是在一个线程中加载图像数据,然后将其传输到另一个线程,用于OpenGL的渲染。这通常涉及到内存管理,确保数据安全地在不同线程间传递,避免数据竞争和同步问题。 **5. 渲染** 在OpenGL...

    易语言将程序转换到线程中执行

    本篇文章将深入探讨如何在易语言中将程序转换到线程中执行,以及这样做的原因和注意事项。 首先,我们需要理解易语言中的线程概念。线程是程序的一部分,它有自己的程序计数器、栈、局部变量和状态,但共享同一块...

Global site tag (gtag.js) - Google Analytics