第三章 线程

来源:互联网 发布:手机文件加密软件2016 编辑:程序博客网 时间:2024/06/05 09:33

运行线程

Thread t = newThread();

t.start();

这个线程上面也不做,没有意义。

运行线程两个方法:

1、         对Thread类派生子类,覆盖其run()方法(继承Thread,重写run,调用Start)

2、         实现Runnable接口,将Runnable对象传递给Thread构造函数。

派生Thread

实现Runnable接口

从线程返回信息

run()方法和start()方法不返回任何值。

竞态条件

到底会得到正确的结果还是异常,或者是一个挂起的程序,这取决于很多因素,包括程序生成了多少线程,系统的CPU和磁盘的速度,系统使用多少个CPU,以及Java虚拟机为不同线程分配时间所用的算法。这些成为竞态条件。

回调

通过调用主类(即启动这个线程的类)中的一个方法来做到的。这被称为回调(callback),因为线程在完成是反过来调用其创建者。

Future、Callable和Executor

创建一个ExecutorService,它会根据需要为你创建线程。可以向ExecutorService提交Callable任务,对于每个Callable任务,会分别得到一个Future。之后可以向Future请求得到任务的结果。

示例3-9:FindMaxTask

<span style="font-size:18px;">import java.util.concurrent.Callable;class FindMaxTask implements Callable<Integer> {  private int[] data;  private int start;  private int end;    FindMaxTask(int[] data, int start, int end) {    this.data = data;    this.start = start;    this.end = end;  }  public Integer call() {    int max = Integer.MIN_VALUE;    for (int i = start; i < end; i++) {      if (data[i] > max) max = data[i];    }    return max;  }}</span>

示例3-10:MultithreadedMaxFinder

<span style="font-size:18px;">import java.util.concurrent.*;public class MultithreadedMaxFinder {  public static int max(int[] data) throws InterruptedException, ExecutionException {        if (data.length == 1) {      return data[0];    } else if (data.length == 0) {      throw new IllegalArgumentException();    }        // split the job into 2 pieces    FindMaxTask task1 = new FindMaxTask(data, 0, data.length/2);    FindMaxTask task2 = new FindMaxTask(data, data.length/2, data.length);        // spawn 2 threads    ExecutorService service = Executors.newFixedThreadPool(2);    Future<Integer> future1 = service.submit(task1);    Future<Integer> future2 = service.submit(task2);            return Math.max(future1.get(), future2.get());  }}</span>

同步

同步块

Synchronized(System.out) {

       //代码块

}

同步方法

publicsynchronized void writeEntry(String message) throws IOException {

       //…

}

同步的替代方式

一:使用局部变量而不是字段

二:使用值不可变变量

三:将非线程安全的类用作为线程安全的类的一个私有字段。

四:使用java.util.concurrent.atomic包中特意设计为保证线程安全但可变的类。

              (AtomicInteger,AtomicLong,AtomicBoolean,AtomicIntegerArray,不使用引用变量,而是把对象存储在一个AtomicReference中,对于映射和列表等集合,可以使用java.util.Collections的方法把他们包装在一个线程安全的版本中,例如有一个集合foo,可以用Collections.synchronizedSet(foo)得到这个集合的一个线程安全视图)。

死锁

同步会带来死锁问题。

如果两个线程需要独占访问同样的一个资源集,而每个线程分别有这些资源的不同子集的锁,就会发生死锁。

线程调度

优先级

当一个低优先级线程正在运行,而一个高优先级线程准备运行时,虚拟机会或早或晚暂停这个低优先级线程,让高优先级线程运行。高优先级线程就抢占了低优先级线程。

为了能让其他线程有机会运行,一个线程有10种方式可以暂停或者指示它准备暂停。这包括:

*可以对I/O阻塞

*可以对同步对象阻塞

*可以放弃

*可以休眠

*可以链接另一个线程

*可以等待一个对象

*可以结束

*可以被更高优先级线程抢占

*可以被挂起

*可以停止

线程池和Executor

利用java.util.concurrent中的Executors类,可以非常容易地建立线程池。只需要将各个任务作为一个Runnable对象提交给这个线程池,你将会得到一个Future对象,可以用来检查任务的进度。

示例3-13显示了GZipRunnable类。它包含一个字段来标识要压缩的文件。run()方法会压缩这个文件并返回。

import java.io.*;import java.util.zip.*;public class GZipRunnable implements Runnable {  private final File input;    public GZipRunnable(File input) {    this.input = input;  }    @Override  public void run() {    // don't compress an already compressed file    if (!input.getName().endsWith(".gz")) {       File output = new File(input.getParent(), input.getName() + ".gz");      if (!output.exists()) { // Don't overwrite an existing file        try ( // with resources; requires Java 7          InputStream in = new BufferedInputStream(new FileInputStream(input));        OutputStream out = new BufferedOutputStream(          new GZIPOutputStream(            new FileOutputStream(output)));         ) {            int b;            while ((b = in.read()) != -1) out.write(b);            out.flush();        } catch (IOException ex) {          System.err.println(ex);        }      }      }   } }


示例3-14 GZipThread是主程序。它构造了线程池,线程数固定为4,并迭代代理命令行中列出的所有文件和目录。这些文件以及这些目录中的文件将用来构建一个GZipRunnable。这个runnable提交到线程池,最终由这4个线程之一处理。

import java.io.*;import java.util.concurrent.*;public class GZipAllFiles {    public final static int THREAD_COUNT = 4;  public static void main(String[] args) {    ExecutorService pool = Executors.newFixedThreadPool(THREAD_COUNT);    for (String filename : args) {      File f = new File(filename);      if (f.exists()) {        if (f.isDirectory()) {          File[] files = f.listFiles();          for (int i = 0; i < files.length; i++) {            if (!files[i].isDirectory()) { // don't recurse directories              Runnable task = new GZipRunnable(files[i]);              pool.submit(task);            }          }        } else {          Runnable task = new GZipRunnable(f);          pool.submit(task);        }      }    }     pool.shutdown();  }}

0 0
原创粉丝点击