并发 多线程 —— 读书笔记——持续更新中

来源:互联网 发布:xcode上c语言的函数库 编辑:程序博客网 时间:2024/06/14 03:42

这是我读《Java编程思想》时,做的读书笔记,还未完善,持续更新中,主要目的是让自己能够理清其中的知识点。

简介

并发是为了提高运行在单处理器上的程序性能,增加上下文切换(从一个任务切换到另一个任务)的代价,以减少任务或线程阻塞带来的开销。
难点在于协调不同线程驱动的任务之间对这些资源的使用,以使得这些资源不会同时被多个任务访问。

    • 简介
    • 术语
    • 相关类接口
      • 任务接口
      • 线程执行器类
    • 对线程的控制
      • 休眠
      • 优先级
      • 让步
      • 后台线程
      • 一些便捷的线程创建方式
      • 捕获异常
    • 资源的共享

术语

在描述将要执行的工作时使用术语“任务”,在引用到驱动任务的具体机制时,使用“线程”。因此,如果在概念级别上讨论系统,用“任务”即可,无须提及驱动机制。

  • 任务:某个线程实际要执行的操作,也就是实现了Runnable接口类中的run()方法以及实现了Callable接口中的call()方法。
  • 线程:驱动任务的具体机制。如Thread类并不执行任何操作,他指示驱动赋予它的任务。

相关类(接口)

多线程的执行需要用到两种类(接口),一种是描述任务的接口,另一种是描述线程执行器的类,将任务提交给线程执行器并启动线程,即可实现线程的创建和启动。
在非常简单的情况下,继承Thread类的类也可实现任务的定义和线程的启动。

任务接口

  • Runnable接口:线程任务定义在实现这个接口的类的run()方法中,它是执行工作的独立任务,并不返回任何值。实现此接口的任务,可用Thread类和Executor类来驱动。
  • Callable接口:实现此接口的类,其任务定义在call()方法中,并且在完成任务后能返回值。Callable是一种具有类型参数的泛型,其类型参数就是从call()方法中返回的值。但此接口必须使用ExecutorService.submit()方法调用它,并会返回一个Future对象。

线程执行器类

  • Thread:将Runnable对象提交给一个Thread构造器即可实现Runnable对象到工作任务的转变。代码示例如下:
//假设LiftOff为一个实现了Runnable接口的类Thread t = new Thread(new LiftOff());//将任务提交给线程t.start();//启动线程//或new Thread(new LiftOff()).start();

就算像上面第二段代码一样,创建Thread类时并未捕获对其的引用,但每个Thread都“注册”了自己,因此确实有一个对它的引用,而且在它的任务推出其run()并死亡之前,垃圾回收器无法清除它。

  • Executor:这个类是客户端和任务之间的一个中介对象,实现了对Thread对象的管理,从而简化了并发编程。Executor允许你管理异部任务的执行二无须显示地管理线程的生命周期。
    一般使用ExecutorService类来执行Runnable 对象,它是一个具有服务生命周期的Executor。它是由Executor的静态方法创建的(使用Executors的工厂方法来创建),然后调用execute(Runnable)来提交任务。主要有以下几种线程池:
    - CachedThreadPool:为每个任务都创建一个线程;可使用shutdown()方法阻止新线程的提交,但会继续执行之前提交的任务,程序将在这些任务执行完成后尽快推出。
    - FixedThreadPool:用有限的线程集来执行所提交的任务;使用这种线程池可一次性预先执行代价高昂的线程分配,限制线程数量。
    - SingleThreadExecutor:线程数量为1的FixedThreadPool;用途:
    1. 在另一个线程中运行长期存活的任务;
    2. 在线程中运行段任务,如更新日志或时间分发线程;
    3. 若提交了多个任务,这些任务将排队。

Executor驱动线程代码示例如下:

    //Runnable接口    //假设LiftOff为一个实现了Runnable接口的类     ExecutorService exec = Executors.newCachedThreadPool();     for(int i = 0; i < 5; i++) {         exec.execute(new LiftOff());    }    //Callable接口    ExecutorService exec = Executors.newCachedThreadPool();    ArrayList<Future<String>> results = new ArrayList<Future<String>>();    for(int i = 0; i < 10; i++) {        results.add(exec.submit(new TaskWithResult(i)))    }

对线程的控制

休眠

  • 这会使任务中止执行一定的时间(参数给定)
  • 在sleep()期间可以抛出InterruptedException异常
  • 如果任务获得了锁,则在sleep()期间任务也不会释放锁
  • Java SE5引入了TimeUnit类,这将sleep的调用变得更加显式,如下代码所示

代码示例:

public class SleepingTask extends LiftOff {    public void run() {        tr {            while(countDown-- > 0) {                System.out.print(status());                //Old-style:                //Thread.sleep(100);                //Java SE5/6-style:                TImeUnit.MILLISECONDS.sleep(100);            }        } catch (InterruptedException e) {            System.err.println("Interrupted");        }    }}

优先级

  • 可以设置线程的优先级给调度器以彰显线程的重要性
  • 优先级高的线程将优先执行,优先级低的线程也会执行,只是执行频率较低
  • 可以通过getPriority()和setPriority()来获取和设置线程的优先级
  • 通过调用Thread.currentThread()来获得对驱动该任务的Thread 对象的引用
  • 优先级要在run()的开头部分设定
  • 为了确保调整优先级之后程序依然可移植至其他平台,只使用MAX_PRIORITY、NORM_PRIORITY和MIN_PRIORITY三种级别

代码示例:

//只给出run()部分public void run() {    THread.currentThread().setPriority(priority);    while(true) {        //执行代码    }}

让步

后台线程

一些便捷的线程创建方式

捕获异常

资源的共享

0 0
原创粉丝点击