多线程(一)多线程基础、多线程状态

来源:互联网 发布:c语言struct用法 编辑:程序博客网 时间:2024/05/22 15:30

多任务:在同一刻运行多个程序的能力。并发执行的进程数目并不是由CPU数目制约的。操作系统将系统将CPU的时间片分配给每一个进程,给人以并行处理的感觉。

多线程程序在较低的层次上扩展了多任务的概念:一个程序同时执行多个任务。通常每个任务称为一个线程,她是线程控制的简称。可以同时运行一个以上线程的程序称为多线程程序。

多进程与多线程的区别:本质的区别在于每个进程拥有自己的一套变量,多个进程的内部数据和状态都是完全独立的,而线程则共享数据。这听起来会有些风险,的确也是这样。然而,共享变量使线程之间的通信比进程之间的通信更有效、更容易。此外,在有些操作系统中,与进程相比,线程更加“轻量级”,创建、撤销一个线程比启动新进程的开销要小得多。线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。

多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。

下面是一个单独的线程中执行一个任务的简单过程:
(1)将任务代码移到实现了Runnable接口的类的run方法中。这个接口非常简单,只有一个方法:

public interface Runnable{    void run();}

可以如下所示实现一个类:

<span style="font-size:18px;">class MyRunnable implements Runnable{    public void run()   {        task code;    } }</span>

(2)创建一个类对象:

<span style="font-size:18px;">Runnable r = new MyRunnable();</span>

(3)由Runnable创建一个Thread对象:

Thread t = new Thread(r);

(4)启动线程

t.start();

多线程中的基础知识:

1.Java中如果我们自己没有产生线程,那么系统就会给我们产生一个线程(主线程,main方法就在主线程上运行),我们的程序都是由线程来执行的。

2. 进程:执行中的程序(程序是静态的概念,进程是动态的概念)。 

3. 线程的实现有两种方式,

  • 第一种方式是继承Thread类,然后重写run方法;
  • 第二种是实现Runnable接口,然后实现其run方法;
  • 第三种应用程序可以使用Executor框架来创建线程池。

前两种方法均需执行线程的start方法为线程分配必须的系统资源、调度线程运行并执行线程的run方法。
实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。

4. 将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个类继承了Thread类之后,该类就叫做一个线程类。 

5. 一个进程至少要包含一个线程。 

6. 对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)。 

7. 对于双核或双核以上的CPU来说,可以真正做到微观并行。

线程状态:

线程可以有以下6中状态

  • New(新创建)
  • Runnable(可运行)
  • Blocked(被阻塞)
  • Waiting(等待)
  • Timed waiting(计时等待)
  • Terminated(被终止)
要想确定一个线程当前的状态,可调用getState方法。


由上图可以看出,一个线程由出生到死亡分为五个阶段:

1).创建状态 

•当用new操作符创建一个新的线程对象时,该线程处于创建状态。 

•处于创建状态的线程只是一个空的线程对象,系统不为它分配资源 

2). 可运行状态 

•执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体—run()方法,这样就使得该线程处于可运行( Runnable )状态。 

•这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。这取决于操作系统给线程提供的运行时间。

一旦一个线程开始运行,它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他线程获得运行机会。线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行线程一个时间片来执行任务。当时间片用完,操作系统剥夺该线程的运行权,并给两一个线程机会运行。

3).被阻塞的线程或等待线程

  • 当一个线程试图获取一个内部的对象锁,而该锁被其他的线程持有,则该线程进入阻塞状态。当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程将变为非阻塞状态。
  • 当线程等待另一个线程通知调度器一个条件时,它自己 进入等待状态。在调用Object.wait, Thread.join,会出现这种情况。被阻塞状态与等待状态是有很大不同的。
  • 有几个方法有一个超时参数时。调用他们导致线程进入计时等待状态。这一状态将一直保持到超时期满或者收到适当的通知。带有超时参数的方法有:Thread.sleep,  Object.wait, Thread.join,   Lock.tryLock,  Condition.await的计时版。

4.  被终止的线程

两个原因之一而被终止:

当线程的run方法执行结束后,该线程自然消亡。

因为一个没有捕获的异常终止了run方法而意外死亡。

可以调用线程的stop方法杀死一个线程。该方法抛出ThreadDeath错误对象,由此杀死线程。但是stop 方法已经过时,不要在自己的代码中调用这个方法。

注意:

1.停止线程的方式:不能使用Thread类的stop方法来终止线程的执行。一般要设定一个变量,在run方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束。 

2.不能依靠线程的优先级来决定线程的执行顺序。 


0 0
原创粉丝点击