欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

多线程一(线程的概念、状态)

程序员文章站 2022-05-05 16:34:02
...

java的重要功能之一就是内部支持多线程,即在一个程序中允许同时运行多个任务。在许多程序设计语言中,多线程都是通过调用依赖于系统的过程或函数来实现的。

  • 线程的概念

一个程序可能包含多个并发运行的任务。线程是指一个任务从头至尾的执行流。线程提供了运行一个任务的机制。对于java而言,可以在一个程序中并发地启动多个线程。这些线程可以在多处理器系统上同时运行,也可以在单处理器系统上同时运行(可以看做是分时),而操作系统负责调度及分配资源给它们。这种安排是切实可行的,因为CPU的大部分时间都是空闲的,例如:在等待用户输入数据时,CPU什么也不做。

多线程可以使程序反应更快、交互性更强、执行效率更高。例如:一个好的文字处理程序允许在输入文字的同时,打印或者保存文件。在一些情况下,即使在单处理器系统上,多线程程序的运行速度也比单线程程序更快。java对多线程程序的创建和运行,以及锁定资源以避免冲突提供了非常好的支持。

当程序作为一个应用程序运行时,java解释器为main方法启动一个线程,还可以在程序中创建附加的线程以执行并发任务。在java中,每个任务都是Runnable接口的一个实例,也称为可运行对象。线程本质上讲就是便于任务执行的对象。

  • 创建任务和线程

​​​​​​​任务就是对象。为了创建任务,必须首先为任务定义一个类。任务类必须实现Runnable接口。Runnable接口非常简单,它只包含一个run方法。需要实现这个方法来告诉系统线程将如何运行。

public class TaskClass implements Runnable{
    ...
    public TaskClass(...){
        ...
    }
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        ...
    }
    ...
}

一旦定义了一个TaskClass,就可以用它的构造方法创建一个任务。例如:

TaskClass task = new TaskClass();

任务必须在线程中执行。Thread类包括创建线程的构造方法以及控制线程的很多有用的方法。使用下面的语句创建任务的线程:

Thread thread = new Thread(task);

然后调用start()方法告诉java虚拟机该线程准备运行,如下所示:

thread.start();

注:任务中的run方法指明如何完成这个任务。java虚拟机会自动调用该方法,无须特意调用它。直接调用run方法只是在同一个线程中执行该方法,而没有新线程被启动。

  • Thread类

​​​​​​​(1)Thread类包含为任务而创建的线程的构造方法,以及控制线程的方法。

Thread():创建一个空线程

Thread(Runnable task):为指定任务创建一个线程

void start():启动线程使run方法被JVM调用

boolean isAlive():测试线程当前是否正在运行

void setPriority(int p):设置线程的优先级(范围从1到10)

void join():等待线程结束

void sleep(long millis):使线程睡眠指定的数

void yield():是线程暂停并允许执行其他线程,即为其他线程临时让出CPU时间。

void interrupt():中断线程

void stop():停止当前线程,不安全的方法,不提倡使用,可以通过给Thread变量赋值null来表明它被停止。

因为Thread类实现了Runnable,所以,可以定义一个Thread的扩展类,并且实现run方法。然后在客户端程序中创建这个类的一个对象,并且调用它的start方法来启动线程。但是,不推荐使用这种方法,因为它将任务和运行任务的机制混在了一起。将任务从线程中分离出来是比较好的设计。

例:

yield方法:

public void run() {
        for(int i=1;i<=num;i++){
            System.out.println(i);
            Thread.yield();
        }
    }

sleep方法:会抛出一个InterruptedException异常,是一个必检异常。

public void run() {
        try{
            for(int i=1;i<=num;i++){
                System.out.println(i);
                if(i>=50)
                    Thread.sleep(1);
            }
        }catch (InterruptedException e) {
        }
    }

join方法:

public void run() {
        Thread thread = new Thread(new PrintChar('c',40));//任务是打印字符,第一个参数为要打印的字符,第二个参数为要打印字符打印次数
        thread.start();
        try{
            for(int i=0;i<num;i++){
                System.out.println(i);
                if(i == 50)
                    thread.join();
            }
        }catch (InterruptedException e) {
        }
    }

JVM总是选择当前优先级最高的可运行线程。较低优先级的线程只有在没有比它更高的优先级的线程运行时才能运行。如果所有可运行线程具有相同的优先级,那将会用循环队列给它们分配相同的CPU份额。这被称为循环调度。

如果总有一个高优先级的线程,或者是相同优先级的线程在运行,那么其他线程可能一直没有机会运行。这种情况称为资源竞争或缺乏状态。为了解决这种情况,运行中的线程必须定时地调用sleep方法或者yield方法,来给低优先级线程或者同等优先级的线程提供运行机会。

  • 线程的状态

​​​​​​​任务在线程中执行,线程可以是以下五种状态之一:新建、就绪、运行、阻塞或结束。

多线程一(线程的概念、状态)

新创建一个线程时,它就进入新建状态。调用线程的start方法启动线程后,它进入就绪状态。就绪线程是可运行的,但可能还没有开始运行,操作系统必须为它分配CPU时间。

就绪线程开始运行时,它就进入运行状态。如果给定的CPU时间用完或调用线程的yield方法,处于运行状态的线程可能就进入就绪状态。

有几种原因可能使线程进程阻塞状态。可能是它自己调用了join方法,sleep方法或wait方法,也可能是其他线程调用了这些方法。它可能是在等待I/O操作的完成。当阻塞行为不起阻塞作用时,阻塞线程可能被重新**。例如:如果线程处于休眠状态并且休眠时间已满,线程就会被重新**并进入就绪状态。

最后,如果一个线程执行完它的run方法,这个线程就被结束。

isAlive方法是用来判断线程状态的方法。如果线程处于就绪、阻塞或运行状态,则返回true;如果线程处于新建并且没有启动的状态,或者已经结束,则返回false。

方法interrupt按下列方式中断一个线程:当线程处于就绪状态或运行状态时,给它设置一个中断标志;当线程处于阻塞状态时,它将被唤醒并进入就绪状态,同时抛出异常java.lang.InterruptedException。