Java多线程基本概念

基本概念

线程与任务的概念不一样。

任务:通常是一些抽象的且离散的工作单元,比如在Web请求中,针对用户的请求需要返回相应的页面是一个任务,在Java中实现Runnable接口的类也是一个任务.

线程:执行任务的实体,可以在单个线程中串行地执行各项任务,例如单线程串行执行Web请求,也可以在为每个请求建立一个线程执行。

任务是一组逻辑工作单元,而线程则是使任务异步执行的机制。

一个简单的例子:当我们定义了一个Runnable对象时,即任务,使用new Thread(Runnable).start()即将任务放到实际的线程中执行。

Executor线程池

Web应用中有两种执行任务的策略:

  • 把所有任务放在单个线程中串行执行:糟糕的响应性和吞吐量。
  • 为每个任务创建一个线程并行的执行:资源管理的复杂性,体现在线程开销大,不稳定。

所以在Java中引入了Executor(线程池)框架来简化线程的管理工作,Executor使用Runnable作为其基本的任务表示形式,在Java类库中,任务执行的主要抽象不是Thread,而是Executor

Executor基于生产者-消费者模式,提交任务的操作相当于生产者(生成待完成的工作单元),执行任务的线程则相当于消费者(执行完这些工作单元),这样就将请求处理任务的提交与任务的实际执行解耦开来。在下面例子中建立一个固定长度的线程池,可以容纳100个线程。

class TaskExecutionWebServer {
    private static final int NTHREADS = 100;
    // 调用Executors中的静态工厂方法可以来创建一个线程池
    private static final Executor exec =
                    Executors.newFixedThreadPool(NTHREADS);

    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(80);
        while (true) {
            final Socket connection = socket.accept();
            Runnable task = new Runnable() {
                public void run() {
                    handleRequest(connection);
                }
            };
            // 通过execute将任务提交到工作队列中,工作线程反复获取执行
            exec.execute(task);
        }
    }
}

执行策略

线程池是指管理一组同构工作线程的资源池。通过重用现有的线程而不是创建新线程,可以在处理多个请求时,分摊在线程创建和销毁过程中产生的巨大开销。另一个额外的好处是,当请求到达时,工作线程已经存在,因为提高了响应性。

  • 工作队列(Work Queue):线程池是与工作队列密切相关的,其中在工作队列中保存了所有等待执行的任务。
  • 工作者线程(Worker Thread):从工作队列中获取一个任务,执行任务,然后返回线程池并等待下一个任务。

在执行策略中定义了任务执行的“What、Where、When、How”等方面,包括:

  • 该任务在哪个线程中执行
  • 任务可以按照什么样的顺序来执行(如优先级、FIFO、LIFO)
  • 有多少个任务可以并发执行
  • ......

生命周期

线程池生命周期(ExecutorService)

我们已经知道了如何创建一个Executor,但并没有讨论如何关闭它。为了解决执行服务的生命周期问题,Executor扩展了ExecutorService接口,添加了一些用于生命周期管理方法。

public interface ExecutorService extends Executor {
    void shutdown();
    List<Runnable> shutdownNow();
    boolean isShutdown();
    //...
}

ExecutorService的生命周期有3种状态:运行、关闭和已终止。shutdown()方法将执行平缓的关闭过程:不再接受新的任务,同时等待已提交任务执行完成(包括未开始执行的任务)。shutdownNow()将尝试取消所有运行的任务。关闭后再提交,由“拒绝执行处理器”处理,或者抛出RejectedExecutionException异常。

任务生命周期(Future)

由于Runnable不能返回一个值或者抛出一个受检查的异常,存在着很大的局限性。所以引入了Callable,它认为主入口点(即call)将返回一个值,并可能抛出一个异常。

又Runnable和Callable描述都是抽象的计算任务,通常经过创建、提交、开始和完成四个阶段,就如ExecutorService为Executor解决生命周期问题,Future也为一个任务解决了生命周期的问题,并提供了相应地方法来判断是否已经完成或取消,以及获取任务的结果和取消任务。

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

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning); // 取消任务
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionExeception,
                        CancellationExeception;
    V get(long timeout, TimeUnit unit) throws
                    InterruptedException, ExecutionExeception
                    CancellationExeception, TimeoutException;

通过调用ExecutorService中得submit方法将返回一个Future代表该任务的生命周期,可用来获得任务的执行结果或者取消任务。也可以显示地为某个指定的Runnable或Callable实例化一个FutureTask。

在将Runnable或Callable提交到Executor的过程中,包含了一个安全发布的过程,即将Runnable或Callable从提交线程发布到最终执行任务的线程。类似地,在获取Future结果的过程中也包含了一个安全发布,即将这个结果从计算它的线程发布到任何通过get获得它的线程。

参考: 书本Java Concurrency in Practice

时间: 2024-11-06 22:41:59

Java多线程基本概念的相关文章

java多线程基本概念与简单实用

概述 程序:Program,是一个静态的概念 进程:Process,是一个动态的概念 进程是程序的一次动态执行过程, 占用特定的地址空间.每个进程都是独立的,由3部分组成cpu,data,code 缺点:内存的浪费,cpu的负担 线程:Thread,是进程中一个"单一的连续控制流程"  (a single sequential flow ofcontrol)/执行路径.线程又被称为轻量级进程(lightweight process). 一个进程可拥有多个并行的(concurrent)线

拨开云雾见天日 —— Java多线程编程概念剖析

说到Java多线程编程,大多数人都会想到继承Thread或实现Runnable编程,new 一个Thread实例,调用start()方法,由OS调用即可.具体过程如下: public class MyThread extends Thread {     @Override     public void run() {         System.out.println("MyThread");     }     public static void main(String[] 

Java多线程编程— 概念以及经常使用控制

多线程能满足程序猿编写很有效率的程序来达到充分利用CPU的目的,由于CPU的空暇时间可以保持在最低限度.有效利用多线程的关键是理解程序是并发运行而不是串行运行的.比如:程序中有两个子系统须要并发运行,这时候就须要利用多线程编程. 线程的运行中须要使用计算机的内存资源和CPU. 一.    进程与线程的概念 这两者的概念,这里仅仅给出自己狭隘的理解: 进程:进程是一个独立的活动的实体,是系统资源分配的基本单元. 它能够申请和拥有系统资源. 每一个进程都具有独立的代码和数据空间(进程上下文). 进程

Java多线程编程— 概念以及常用控制

多线程能满足程序员编写非常有效率的程序来达到充分利用CPU的目的,因为CPU的空闲时间能够保持在最低限度.有效利用多线程的关键是理解程序是并发执行而不是串行执行的.例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程.线程的运行中需要使用计算机的内存资源和CPU. 一.    进程与线程的概念 这两者的概念,这里只给出自己狭隘的理解: 进程:进程是一个独立的活动的实体,是系统资源分配的基本单元.它可以申请和拥有系统资源.每个进程都具有独立的代码和数据空间(进程上下文).进程的切换会有

Java多线程——锁概念与锁优化

为了性能与使用的场景,Java实现锁的方式有非常多.而关于锁主要的实现包含synchronized关键字.AQS框架下的锁,其中的实现都离不开以下的策略. 悲观锁与乐观锁 乐观锁.乐观的想法,认为并发读多写少.每次操作的时候都不上锁,直到更新的时候才通过CAS判断更新.对于AQS框架下的锁,初始就是乐观锁,若CAS失败则转化为悲观锁. 悲观锁.悲观的想法,认为并发写多读少.每次操作数据都上锁,即使别人想读也要先获得锁才能读.对于1.6以前的synchronized关键字,则是悲观锁的实现之一.

Java多线程&amp;lt;1&amp;gt;

1.Java多线程的概念: 线(Thread):它指的是一个任务的从开始执行流程到结束. 穿线提供执行任务的机构.供java条款.在一个程序可以启动多个并发线程.候执行. 在单处理器系统中,多个线程共享CPU时间,而操作系统负责调度及分配资源给它们.当程序作为一个应用程序来执行时.JAVA解释器为main开启一个线程. 当程序为Applet执行时,Web浏览器启动一个线程来执行applet.还能够在程序中创建附加的线程以执行并发任务. 在JAVA中,每一个任务都是Runnable的实例. 2.创

Java多线程编程(学习笔记)

一.说明 周末抽空重新学习了下多线程,为了方便以后查阅,写下学习笔记. 有效利用多线程的关键是理解程序是并发执行而不是串行执行的.例如:程序中有两个子系统需要并发执行,这时候需要利用多线程编程. 通过多线程的使用,可以编写出非常高效的程序.但如果创建了太多的线程,程序执行的效率反而会降低. 同时上下文的切换开销也很重要,如果创建太多的线程,CPU花费在上下文的切换时间将对于执行程序的时间. 二.Java多线程编程 概念 在学习多线程时,我们应该首先明白另外一个概念. 进程:是计算机中的程序关于某

Java多线程&lt;1&gt;

1.Java多线程的概念: 线程(Thread):是指一个任务从头到尾的执行流.线程提供了运行一个任务的机制.对于java而言,可以在一个程序中并发的启动多个线程,这些线程可以在多处理器上同时运行. 在单处理器系统中,多个线程共享CPU时间,而操作系统负责调度及分配资源给它们.当程序作为一个应用程序来运行时,JAVA解释器为main开启一个线程.当程序为Applet运行时,Web浏览器启动一个线程来运行applet.还可以在程序中创建附加的线程以执行并发任务.在JAVA中,每个任务都是Runna

Java 多线程详解(一)------概念的引入

这是讲解 Java 多线程的第一章,我们在进入讲解之前,需要对以下几个概念有所了解. 1.并发和并行 并行:指两个或多个时间在同一时刻发生(同时发生): 并发:指两个或多个事件在一个时间段内发生. 在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的. 而在多个 CPU 系统中,则这些可以并发执行的程序便可以分配到多个处