1Z0-809复习 第7章 并发

Concurrency

Introducing Threads

Distinguishing Thread Types

Introducing Threads

Understanding Thread Concurrency

Introducing Runnable

Creating a Thread

Polling with Sleep

daemon threads:不阻止JVM退出的线程

Creating Threads with the ExecutorService

ExecutorService是用来创建和管理线程。
首先获得一个ExecutorService接口的实例。
然后把task交给这个service去执行。
ExecutorService框架包含很多有用的特性,例如线程池和实行计划。

Introducing the Single-Thread Executor

public class ZooInfo {
    public static void main(String[] args) {
        ExecutorService service = null;
        try {
            service = Executors.newSingleThreadExecutor();
            System.out.println("begin");
            service.execute(() -> System.out.println("Printing zoo inventory"));
            service.execute(() -> {for(int i=0; i<3; i++)
                System.out.println("Printing record: "+i);}
                    );
            service.execute(() -> System.out.println("Printing zoo inventory"));
            System.out.println("end");
        } finally {
            if(service != null) service.shutdown();
        }
    }
}
begin
Printing zoo inventory
Printing record: 0
Printing record: 1
end
Printing record: 2
Printing zoo inventory
  • 通过工厂类Executors获得ExecutorService接口的实例类newSingleThreadExecutor
  • service是一个单线程Executor,因此传给它的3个task是按顺序执行的。
  • begin,end是main这个Thread的结果,跟service不是同一个Thread, 因此end在当中出现了。

虽然单线程Executor是按照提交顺序执行任务,但编程时最好避免依赖这个原则。 因为多线程Executor并不按照这个原则

Shutting Down a Thread Executor

Thread Executor会在执行第一个task的时候产生一个非daemon线程,
因此如果不调用shutdown(),应用会永远没有结束。

执行了shutdown()后,Executor不再接受任何新的线程。 isShutdown()也是true了。isTerminated()还是false。
当Executor执行完了所有线程,则isTerminated()也是true。

shutdownNow(),立即结束所有正在执行和未执行的thread。
返回一个Runnable的list,内容是所有提交给了Executor但还没有开始的线程。

Q:那如果不是Runnable而是Callable的线程怎么返回?

ExecutorService没有实现AutoCloseable接口,因此没法用try-with-resources
只能在finally块中关掉。

问题来了,如果这个Exectuor是静态的。
那么必须自定义一个静态方法,随时随地可以调用来结束应用。

Submitting Tasks

除了execute(Runnable command)之外,还可以用submit。
submit返回一个Future对象,用来判断任务是否结束。
当线程结束后,这个对象可以用来返回一个泛型结果对象。

还可以用invokeAll/invokeAny
参数Collection里的所有线程全部结束,还是其中之一结束。 这两个方法内的线程是同期的。

Waiting for Results

利用Future对象的方法, boolean isDone() V get(long timeout,TimeUnit unit) 可以判断线程是否执行完了以及取得Callable的结果。

Introducing Callable
service.submit(() -> {Thread.sleep(1000); return null;});
service.submit(() -> {Thread.sleep(1000);});

Thread.sleep(1000);会抛出一个InterruptedException, 因此要么带有try/catch,要么本身能够支持checked Exception。

上面行编译通过是因为有显式的返回值,编译器认为其是Callable表达式。 因为Call表达式支持checked exception,所以通过。

下面行通不过编译是因为没有return,编译器认为是Runnable表达式。 因此编译不过。

Waiting for All Tasks to Finish

Scheduling Tasks

ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

用ScheduledExecutorService来实现。

schedule(Callable<V> callable,long delay, TimeUnit unit)
schedule(Runnable command,long delay, TimeUnit unit)

Future<?> result2 = service.schedule(() -> "Monkey";, 8, TimeUnit.MINUTES);

返回的是ScheduledFuture
在8分钟后执行任务。

service.scheduleAtFixedRate(command,5,1,TimeUnit.MINUTE);

在一个初始5分钟的delay后,每隔1分钟执行一次command。
如果线程执行时间超过间隔时间,ScheduledExecutorService还是会按照间隔时间发起一个新的线程。
线程会连续执行。

scheduleAtFixedDelay() 和scheduleAtFixedRate() 都不接受Callable作为参数。
只要Executor没有完了,会不断产生Future对象。

Increasing Concurrency with Pools

  1. newCachedThreadPool()
  2. newFixedThreadPool(int nThreads)
  3. newScheduledThreadPool(int nThreads)

上面3个方法创建带线程池的Executor。
newCachedThreadPool() 创建的线程池没有大小。
两种情况下创建新的线程,
1是接收到创建线程的要求,2是所有线程都忙。
常用在需要习性大量短生命周期的非同期任务。

长生命周期不适用是因为有可能因此产生大量线程。

newScheduledThreadPool()的scheduleAtFixedRate()
跟ScheduledExecutorService的scheduleAtFixedRate()有着细微的差别
当执行时间超过间隔时间时,间隔时间不变,多个线程同时执行。

Choosing a Pool Size

一般如果是CPU的任务的话,线程池不要太大,
但如果是访问数据库,文件之类线程,因为CPU大多是在等待,
线程池可以大一点。
可用CPU数量:Runtime.getRuntime().availableProcessors()

Synchronizing Data Access

public class SheepManager {
    private int sheepCount = 0;
    private void incrementAndReport() {
        System.out.print((++sheepCount)+" ");
    }
    public static void main(String[] args) {
        for (int x=0;x<10;x++){
            ExecutorService service = null;
            try {
                service = Executors.newFixedThreadPool(20);
                SheepManager manager = new SheepManager();
                for(int i=0; i<10; i++)
                    synchronized(manager) {
                        service.submit(() -> manager.incrementAndReport());
                    }
            } finally {
                if(service != null) service.shutdown();

            }
        }

    }
}

对于sheepCount这个变量,由于多线程访问,且非线程安全
打印结果有可能是下述结果。1 2 3 6 5 4 4 7 8 9
线程同时执行造成的不确定结果称作竞争危害(race condition)
还有一个现象就是,6在5之前被打印了。

Protecting Data with Atomic Classes

Atomic首先是operation的属性,

Improving Access with Synchronized Blocks

for(int i=0; i<10; i++) {
    synchronized(manager) {
        service.submit(() -> manager.incrementAndReport());
    }
}
private void incrementAndReport() {
    synchronized(this) {
        System.out.print((++sheepCount)+" ");
    }
}

上面个解决不了是因为manager被同期话了,也就是说线程创建是一个一个了。
但实行没有。
下面一个就是针对实行时按一个线程。

Synchronizing Methods

Understanding the Cost of Synchronization

Using Concurrent Collections

Introducing Concurrent Collections

Understanding Memory Consistency Errors

Working with Concurrent Classes

Obtaining Synchronized Collections

Working with Parallel Streams

Creating Parallel Streams

Processing Tasks in Parallel

Processing Parallel Reductions

Managing Concurrent Processes

Creating a CyclicBarrier

Applying the Fork/Join Framework

Identifying Threading Problems

Understanding Liveness

Managing Race Conditions

1.D,F
2.A,C,D,F
3.A
4.C 我觉得第一个是递增,第二个结果会变
5.D
6.B,D
7.不会,x2应该出错?
8.G,findAny是返回sort操作最快的元素。
9.E 有返回值应该是RecursiveTask,因为Pool的大小是1,所以相等于singlethread
10 不会
11 A,F
12 A
13.A  the stream created by flatMap() is a new stream that is not parallel by default, even though its elements are parallel streams. Therefore, the performance will be single-threaded and G is correct.
14.D
15.CEG
16.A A是错的,printed本身也是无序的
17.B 因为是单线程的
18.F
19.ADF 一个是instance,一个是this,应该是同一个东西。E不对是因为增加没有用多线程管理
The synchronized object on line k1 is TicketManager.class, while the synchronized object
on line k4 is the instance of TicketManager. 
所以F是错的
20 AD
21 ACDE 
22 F