多线程知识点总结
- 多线程知识点总结
-
- (1)实现Runnable接口、或者继承Thread类;重写里面的run方法。
- (2)join用于等待被调用线程执行结束后,再执行调用线程下面的代码。
- (3)interrupted中断,中断线程检测isInterrupted标志, 如果线程处于等待状态,则会捕获InterruptedException异常。
- (4)守护线程, t.setDaemon(true)。
- (5)synchronized的概念,以及要解决的问题:多线程竞争的问题。但没有解决线程之间相互协调的问题。
- (6)synchronized----wait----notify/notifyAll。解决线程之间相互协调的问题。
- (7)异常处理的原则:工具类只需抛出异常,谁调用谁负责,谁处理。
- (8)ReentrantLock
- (9)ReadWriteLock
- (10)Condition
- (11)Concurrent集合
- (12)Atomic原子类
- (13)ExcutorServices
- (14)Future接口
- (15)CompletableFuture
- (16)Fork/Join
【说明:网络课程笔记。】
多线程知识点总结
(1)实现Runnable接口、或者继承Thread类;重写里面的run方法。
public class ThreadDemo01 {
public static void main(String[] args) {
System.out.println("main start...");
Thread t=new Thread(){
@Override
public void run() {
System.out.println("thread run...");
System.out.println("thread end.");
}
};
t.start();
System.out.println("main end...");
}
}
(2)join用于等待被调用线程执行结束后,再执行调用线程下面的代码。
public class JoinDemo02 {
public static void main(String[] args) throws InterruptedException {
List<Thread> threads=new ArrayList<>();
for (String name : Arrays.asList("Bob","Alice","Tom")){
threads.add(new HelloThread2(name));
}
System.out.println("main start...");
for (Thread t : threads) {
t.start();
//主线程调用t线程的join.强制让一个线程执行完,再进行器后续
//的操作,有强制串行化的效果。
// t.join();
}
//修改后的执行效果,让主线程在子线程执行结束后,才能执行结束代码
for (Thread t:threads){
t.join();
}
System.out.println("main end.");
}
}
class HelloThread2 extends Thread{
String name;
public HelloThread2(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("Hello, "+name+"!");
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Goodbye,"+name+"!");
}
}
(3)interrupted中断,中断线程检测isInterrupted标志, 如果线程处于等待状态,则会捕获InterruptedException异常。
public class InterruptDemo {
public static void main(String[] args) throws InterruptedException {
Thread t=new HelloThread3();
t.start();
//主线程等待1秒
Thread.sleep(1000);
//主线程发起中断t线程通知。
//主线程通过调用t线程的interrupt方法中断t线程。
t.interrupt();
}
}
class HelloThread3 extends Thread {
@Override
public void run() {
//中断线程需要通过检测isInterrupted标志
while (!isInterrupted()) {
System.out.println("Hello");
/**
* 如果线程处于等待状态,该线程会捕获InterruptedException异常。
*/
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
/**
* 捕获到InterruptedException说明有其他线程对其调用
* 了interrupt()方法,通常情况下线程应该立刻结束运行。
*/
return;
}
}
}
}
(4)守护线程, t.setDaemon(true)。
public class DaemonThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("main start...");
TimerThread t=new TimerThread();
//设置t线程为守护线程,当主线程结束后,守护线程也跟随结束线程。
/**
* 守护线程是为其他线程服务的线程
* 所有非守护线程都执行完毕后,虚拟机退出
* 守护线程不能持有资源(如打开文件等)
*/
t.setDaemon(true);
t.start();
Thread.sleep(5000);
System.out.println("main end.");
}
}
class TimerThread extends Thread{
@Override
public void run() {
while (true){
System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH::mm::ss")));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(5)synchronized的概念,以及要解决的问题:多线程竞争的问题。但没有解决线程之间相互协调的问题。
public class SyncDemo01 {
public static void main(String[] args) throws InterruptedException {
Thread t1=new AddThread();
Thread t2=new DecThread();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(Counter.count);
}
public static class Counter{
//锁对象的典型声明。
public static final Object LOCK=new Object();
final static int LOOP=100000;
public static int count=0;
}
public static class AddThread extends Thread{
@Override
public void run() {
for (int i=0;i<Counter.LOOP;i++){
/* //未加锁的时候,结果不为0
Counter.count+=1;*/
//加锁后的最终结果为0
synchronized (Counter.class){
Counter.count+=1;
// System.out.println("加1后,结果为:"+Counter.count);
}
}
}
}
public static class DecThread extends Thread{
@Override
public void run() {
for (int i = 0; i < Counter.LOOP; i++) {
/* //未加锁的时候,结果不为0
Counter.count+=1;*/
//加锁后的最终结果为0
synchronized (Counter.class){
Counter.count-=1;
// System.out.println("减1后,结果为:"+Counter.count);
}
}
}
}
}
(6)synchronized----wait----notify/notifyAll。解决线程之间相互协调的问题。
public class WaitNotifyDemo01 {
public static void main(String[] args) throws InterruptedException {
TaskQueue taskQueue = new TaskQueue();
WorkerThread worker = new WorkerThread(taskQueue);
worker.start();
//add task:
taskQueue.addTask("Bob");
Thread.sleep(1000);
taskQueue.addTask("Alice");
Thread.sleep(1000);
taskQueue.addTask("Tim");
Thread.sleep(1000);
worker.interrupt();
worker.join();
System.out.println("End");
}
}
class TaskQueue {
//定义一个任务队列,用于后面的添加任务和获取任务。
final Queue<String> queue = new LinkedList<>();
//添加任务方法,
public synchronized void addTask(String s) {
this.queue.add(s);
//notify通知之前处于wait状态的线程,让其进入唤醒的状态。
//此时被锁定的方法,还持有该锁,待该锁释放后。之前被唤醒的方法,可以竞争到该锁。
/*this.notify();*/
this.notifyAll();
}
public synchronized String getTask() throws InterruptedException {
while (queue.isEmpty()) {
//wait会释放this锁,这点很重要。
//如果队列里面没有元素,需要进入等待状态,并释放锁。
//待另一个线程往队列里面添加元素后,对其进行唤醒。
System.out.println("线程开始进行等待状态。。。");
this.wait();
}
return queue.remove();
}
}
class WorkerThread extends Thread {
TaskQueue taskQueue;
public WorkerThread(TaskQueue taskQueue) {
this.taskQueue = taskQueue;
}
@Override
public void run() {
while (!isInterrupted()) {
String name;
try {
name = taskQueue.getTask();
} catch (InterruptedException e) {
/*e.printStackTrace();*/
System.out.println("线程被主线程中断,退出当前线程中。。。");
return;
}
String result = "Hello, " + name + "!";
System.out.println(result);
}
}
}
(7)异常处理的原则:工具类只需抛出异常,谁调用谁负责,谁处理。
在以下片段中,并没有处理异常,而是选择抛出InterruptedException的异常。
class TaskQueue {
//定义一个任务队列,用于后面的添加任务和获取任务。
final Queue<String> queue = new LinkedList<>();
//添加任务方法,
public synchronized void addTask(String s) {
this.queue.add(s);
//notify通知之前处于wait状态的线程,让其进入唤醒的状态。
//此时被锁定的方法,还持有该锁,待该锁释放后。之前被唤醒的方法,可以竞争到该锁。
/*this.notify();*/
this.notifyAll();
}
public synchronized String getTask() throws InterruptedException {
while (queue.isEmpty()) {
//wait会释放this锁,这点很重要。
//如果队列里面没有元素,需要进入等待状态,并释放锁。
//待另一个线程往队列里面添加元素后,对其进行唤醒。
System.out.println("线程开始进行等待状态。。。");
/*特别注意,此处选择抛出异常,而不是处理异常。*/
/*特别注意,此处选择抛出异常,而不是处理异常。*/
/*特别注意,此处选择抛出异常,而不是处理异常。*/
this.wait();
}
return queue.remove();
}
}
(8)ReentrantLock
ReentrantLock可以替换synchronized
ReentrantLock获取锁更安全
ReentrantLock是类,需要创建一个锁对象lock。在需要加锁的地方加锁,调用lock.lock();。在finally块中,调用lock.unlock释放锁。
/*
lock.lock();
使用
try{}finally{lock.unlock()}
来保证锁能够正常的释放。
*/
/**
*ReentrantLock:
* 可重入锁,一个线程可多次获取同一个锁。
* lock()方法可获取锁。
* tryLock()方法可尝试获取锁并可指定超时。不用一直等到下去。。
*/
import sun.java2d.pipe.LoopBasedPipe;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo1 {
public static int LOOP=100000;
public static void main(String[] args) throws InterruptedException {
Counter counter=new Counter();
Thread t1=new Thread(){
@Override
public void run() {
for (int i=0;i<LOOP;i++) {
counter.add(1);
}
}
};
Thread t2=new Thread(){
@Override
public void run() {
for (int i=0;i<LOOP;i++) {
counter.dec(1);
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.getValue());
}
}
class Counter{
//新建一个ReentrantLock实例,向上造型为Lock接口。
private Lock lock=new ReentrantLock();
private int value=0;
public void add(int m){
//获得当前对象的锁。必须在try之外完成。
lock.lock();
try {
this.value+=m;
} finally {
//释放锁。必须在finally里面完成。
lock.unlock();
}
}
public void dec(int m){
//在try之外加锁
lock.lock();
try {
this.value-=m;
} finally {
//在finally里面释放锁。
lock.unlock();
}
}
public int getValue() {
return value;
}
}
class Counter2{
final Lock lock=new ReentrantLock();
public void inc() throws InterruptedException {
if (lock.tryLock(1, TimeUnit.SECONDS)){
try {
//需同步代码块。
} finally {
lock.unlock();
}
}
}
}
/**
* 总结
* ReentrantLock可以替代synchronized
* ReentrantLock获取锁更安全
* 必须使用try...finally保证正确获取和释放锁
*/
(9)ReadWriteLock
允许多个线程同时读,但只要有一个线程在写,其他线程就必须等待。
使用ReadWriteLock可以解决:
值允许一个线程写入(其他线程既不能写入也不能读取)
没有写入时,多个线程允许同时读(提高性能)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 允许多个线程同时读,但只要有一个线程在写,其他线程就必须等待。
* 使用ReadWriteLock可以解决:
* 只允许一个线程写入(其他线程既不能写入也不能读取)
* 没有写入时,多个线程允许同时读(提高性能)
*/
public class ReadWriteLockDemo01 {
final static int LOOP=10000000;
public static void main(String[] args) throws InterruptedException {
Counter counter=new Counter();
Thread t1=new Thread(){
@Override
public void run() {
for (int i = 0; i < LOOP; i++) {
counter.add(1);
}
}
};
Thread t2=new Thread(){
@Override
public void run() {
for (int i = 0; i < LOOP; i++) {
counter.dec(1);
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.get());
System.out.println("main end.");
}
}
class Counter{
private static int value=0;
final ReadWriteLock lock=new ReentrantReadWriteLock();
final Lock rLock=lock.readLock();
final Lock wLock=lock.writeLock();
public void add(int m){
wLock.lock();
try {
value+=m;
} finally {
wLock.unlock();
}
}
public void dec(int m){
wLock.lock();
try {
value-=m;
} finally {
wLock.unlock();
}
}
public int get(){
rLock.lock();
try {
return value;
} finally {
rLock.unlock();
}
}
}
/**
* ReadWriteLock适用条件:
* 同一个实例,有大量线程读取,仅有少量线程修改。
* 总结
* 使用ReadWriteLock可以提高读取效率:
* ReadWriteLock只允许一个线程写入
* ReadWriteLock允许多个线程同时读取
* ReadWriteLock适合读多写少的场景
*/
(10)Condition
Condition是一个类,使用的时候,需要创建一个condition对象,需要用ReentrantLock的对象来创建condition对象。
condition对象的await和signal和wait、notify的使用方式及效果一致。
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo {
public static void main(String[] args) throws InterruptedException {
TaskQueque taskQueque=new TaskQueque();
WorkerThread worker=new WorkerThread(taskQueque);
worker.start();
//添加新任务:
taskQueque.addTask("刘备");
Thread.sleep(1000);
taskQueque.addTask("张飞");
Thread.sleep(1000);
taskQueque.addTask("关羽");
Thread.sleep(1000);
worker.interrupt();
worker.join();
System.out.println("main end.");
}
}
class TaskQueque{
//定义一个任务队列,用于后面的添加任务和获取任务。
final Queue<String> queue = new LinkedList<>();
final Lock lock =new ReentrantLock();
final Condition condition=lock.newCondition();
public String getTask() throws InterruptedException {
lock.lock();
try {
while (this.queue.isEmpty()) {
//相当于wait---notify里面的wait.
condition.await();
}
return queue.remove();
} finally {
lock.unlock();
}
}
public void addTask(String name){
lock.lock();
try {
this.queue.add(name);
//相当于wait--notifyAll中调用notifyAll方法。
condition.signalAll();
} finally {
lock.unlock();
}
}
}
class WorkerThread extends Thread{
TaskQueque taskQueque;
public WorkerThread(TaskQueque taskQueque) {
this.taskQueque = taskQueque;
}
@Override
public void run() {
String name;
while (!isInterrupted()){
try {
name=taskQueque.getTask();
} catch (InterruptedException e) {
System.out.println("线程被中断线程中断中。。。");
break;
}
String result="Hello, "+name+"!";
System.out.println(result);
}
}
}
/**
* Condition
* Condition.await --Condition.signal--signalAll原理和
* wait -------------notify--------------notifyAll一致
* await()会释放当前锁,进入等待状态
* signal()会唤醒某个等待线程
* signalAll()会唤醒所有等待线程
* 唤醒线程从await()返回后需要重新获得锁
*
*/
/**
* 总结:
* Condition对的await---signalAll可以替代wait - notifyAll。
* Condition对象必须从ReentrantLock对象获取
* ReentrantLock+Condition可以替代synchronized+wait/notify
*/
(11)Concurrent集合
java.util.concurrent提供了线程安全的Blocking集合
ArrayBlockingQueue
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/*
多线程
java.util.concurrent提供了线程安全的Blocking集合
ArrayBlockingQueue
*/
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> taskQueue=new ArrayBlockingQueue<>(100);
WorkerThread worker=new WorkerThread(taskQueue);
worker.start();
//add task:
taskQueue.put("Bob");
Thread.sleep(1000);
taskQueue.put("Alice");
Thread.sleep(1000);
taskQueue.put("Tim");
Thread.sleep(1000);
worker.interrupt();
worker.join();
System.out.println("main end.");
}
}
class WorkerThread extends Thread{
BlockingQueue<String> taskQueue;
public WorkerThread(BlockingQueue<String> taskQueue) {
this.taskQueue = taskQueue;
}
@Override
public void run() {
while (!isInterrupted()){
String name;
try {
name=taskQueue.take();
} catch (InterruptedException e) {
System.out.println("收到主线程中断信号,中断中。。。");
break;
}
String result="Hello, "+name+"!";
System.out.println(result);
}
}
}
/*
java.util.concurrent提供了线程安全的Blocking集合:
Interface Non-thread safe Thread safe
List ArrayList CopyOnWriteArrayList
Map HashMap ConcurrentHashMap
Set HashSet,TreeSet CopyOnWriteArraySet
Queue ArrayDeque,LinkedList ArrayBlockingQueue,LinkedBlockingQueue
Deque ArrayDeque,LinkedList LinkedBlockingDeque
-----------------------------------------
//不推荐使用。。
Collections
java.util.Collections工具类还提供了旧的线程安全集合转换器
Map unsafeMap = new HashMap();
Map threadSafeMap = Collections.synchronizedMap(unsafeMap);
-----------------------------------------
总结
使用java.util.concurrent提供的Blocking集合可以简化多线程编程
多线程同时访问Blocking集合是安全的
尽量使用JDK提供的concurrent集合,避免自己编写同步代码
*/
(12)Atomic原子类
java.util.concurrent.atomic提供了一组原子类型操作
/*
Atomic
java.util.concurrent.atomic提供了一组原子类型操作
AtomicInteger
int addAddGet(int delta)
int incrementAndGet()
int get()
int compareAndSet(int expect,int update)
------------------------------------------
Atomic 类可以实现:
无锁(lock-free)实现的线程安全(thread-safe)访问
*/
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicDemo {
final static int LOOP=1000000;
public static void main(String[] args) throws InterruptedException {
Counter counter=new Counter();
Thread t1=new Thread(){
@Override
public void run() {
for (int i = 0; i < LOOP; i++) {
counter.add(1);
}
}
};
Thread t2=new Thread(){
@Override
public void run() {
for (int i = 0; i < LOOP; i++) {
counter.dec(1);
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.get());
System.out.println("main end.");
}
}
class Counter{
private AtomicInteger value=new AtomicInteger(0);
public int add(int m){
return this.value.addAndGet(m);
}
public int dec(int m){
return this.value.addAndGet(-m);
}
public int get(){
return this.value.get();
}
}
/*
总结
使用java.util.atomic提供的原子操作可以简化多线程编程:
AtomicInteger/AtomicLong/AtomicIntegerArray等
原子操作实现了无锁的线程安全
适用于计数器,累加器等。
*/
(13)ExcutorServices
/*
ExecutorService----线程池服务
JDK提供了ExecutorService接口表示线程池
---------------------------------------------
多线程
Java语言内置多线程支持
创建线程需要操作系统资源(线程资源,栈空间。。。)
频繁创建和销毁线程需要消耗大量时间
---------------------------------------------
线程池:
线程池维护若干个线程,处于等待状态
如果有新任务,就分配一个空闲线程执行
如果所有线程都处于忙碌状态,新任务放入队列等待。
---------------------------------------------
常用ExecutorService:
FixedThreadPool:线程数固定的线程池。
CachedThreadPool:线程数根据任务动态调整的线程池
SingleThreadExecutor:仅单线程执行
--------------------------------------------
*/
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorServiceDemo {
public static void main(String[] args) {
/*
固定大小的线程池
ExecutorService executor= Executors.newFixedThreadPool(4);
executor.submit(task1);
executor.submit(task2);
executor.submit(task3);
*/
}
}
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
/*//固定大小的线程池,比如,固定为3个线程
ExecutorService executor= Executors.newFixedThreadPool(3);*/
/* //单个线程的线程池
ExecutorService executor= Executors.newSingleThreadExecutor();*/
/*//根据任务数动态确定线程数量的线程池,但不能确定其上限
ExecutorService executor= Executors.newCachedThreadPool();*/
//浏览newCachedThreadPool()源代码,得到可以确定动态的线程池,并确定其上限的线程池。
//但该方法在测试后,发现限制线程上限,实际执行过程中,偶尔会引发异常。
/* ExecutorService executor=new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());*/
ExecutorService executor=new ThreadPoolExecutor(0, 10,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
executor.submit(new PrintTask("Bob"));
executor.submit(new PrintTask("Alice"));
executor.submit(new PrintTask("Tim"));
executor.submit(new PrintTask("Robot"));
Thread.sleep(10000);
executor.shutdown();
}
}
class PrintTask implements Runnable{
String name;
public PrintTask(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Hello, "+name+"!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/*
ScheduledThreadPool:
一个任务可以定期反复执行
每隔多长时间开始一个新的重复任务。每隔5秒钟执行
AtFixedRate
待任务执行完成后,间隔多长时间开始一个重复的新任务。间隔5秒钟执行
WithFixedDelay
-----------------------------------------------------------
问题:
FixedRate模式下,如果任务执行时间过长,后续任务会不会并发执行?
如果任务抛出了异常,后续任务是否继续执行?
----------------------------------------------------------
被取代的类。。。
java.util.Timer
一个Timer对应一个Thread
必须在主线程结束时调用Timer.cancel()
----------------------------------------------------------
总结:
JDK提供了ExecutorService实现了线程池功能
线程池内部维护一组线程,可以高效执行大量小任务
Executors提供了静态方法创建不同类型的ExecutorService
必须调用shutdown()关闭ExecutorService
ScheduledThreadPool可以定期调度多个任务。
*/
public class ScheduleDemo {
public static void main(String[] args) {
ScheduledExecutorService executor= Executors.newScheduledThreadPool(3);
executor.scheduleAtFixedRate(new HelloTask("Bob"),2,5, TimeUnit.SECONDS);
executor.scheduleAtFixedRate(new HelloTask("哈哈"),2,5, TimeUnit.SECONDS);
executor.scheduleWithFixedDelay(new HelloTask("Alice"),2,5,TimeUnit.SECONDS);
}
}
class HelloTask implements Runnable{
String name;
public HelloTask(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("Hello, "+name+"!It is "+ LocalTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Goodbye, "+name+"It is "+LocalTime.now());
}
}
(14)Future接口
Future接口:
表示一个未来可能会返回的结果。
/*
Callable<T>接口
class Task implements Callable<String>{
public String call() throws Exception{
return longTimeCalculation();
}
}
----------------------------------------------
相比于Runnable(需要重写run方法)接口,提供了一个返回值,可以获取线程
运行后的一个结果。Callable<T>需要重写call方法。
----------------------------------------------
如何获得异步执行的结果呢?
Callable<String> task=new Task();
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<String> future = executor.submit(task);
//可能阻塞
String result=future.get();
----------------------------------------------
Future<V>接口:
表示一个未来可能会返回的结果
get()
get(long timeout,TimeUnit unit)
cancel(boolean mayInterruptIfRunning)
isDone();
-----------------------------------------------
Runnable vs Callable
不需要返回结果 需要返回结果
-----------------------------------------------
提交Callable任务,可以获得一个Future对象
可以用Future在将来某个时刻获得结果
*/
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.*;
public class FutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor= Executors.newFixedThreadPool(3);
DownloadTask task=new DownloadTask("http://news.baidu.com/");
Future<String> future=executor.submit(task);
String html=future.get();
System.out.println(html);
executor.shutdown();
}
}
class DownloadTask implements Callable<String>{
String url;
public DownloadTask(String url) {
this.url = url;
}
@Override
public String call() throws Exception {
System.out.println("Start download "+url+"...");
URLConnection conn=new URL(this.url).openConnection();
conn.connect();
try(BufferedReader reader=new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"))){
String s=null;
StringBuilder sb=new StringBuilder();
while ((s=reader.readLine())!=null){
sb.append(s).append("\n");
}
return sb.toString();
}
}
}
(15)CompletableFuture
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
/*
使用Future获得异步执行结果:
Future<String> future = executor.submit(task);
String result=future.get();//可能阻塞
while(!future.isDone()){
Thread.sleep(1);
}
String result = future.get();//可能阻塞
----------------------------------------------
使用Future获得异步执行结果:
阻塞方法:get()
轮询:isDone()
----------------------------------------------
CompletableFuture接口:
里面通过回调函数来获得结果。
CompletableFuture<String> cf=getCompletableFutureFromSomewhere();
cf.thenAccept(result->{sout("正常运行获得异步结果:"+result)});
cf.exceptionally(t->{sout("运行发生异常:"+t.getMassage())}
*/
public class CompletableFutureDemo {
public static void main(String[] args) {
CompletableFuture<Float>
getStockFuture=CompletableFuture.supplyAsync(new StockSupplier());
getStockFuture.thenAccept(x->{
System.out.println("Current price:"+x);
});
getStockFuture.exceptionally(e->{
System.out.println("Error:"+e.getMessage());
return Float.NaN;
});
getStockFuture.join();
}
}
class StockSupplier implements Supplier<Float>{
@Override
public Float get() {
String url="http://hq.sinajs.cn/list=sh000001";
System.out.println("GET:"+url);
try {
String result=DownloadUtil.download(url);
String[] ss=result.split(",");
return Float.parseFloat(ss[3]);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
/*
CompletableFuture的优点:
异步任务结束时,会自动回调某个对象的方法
异步任务出错时,会自动回调某个对象的方法
主线程设置好回调后,不再关心异步任务的执行
---------------------------------------
CompletableFuture基本用法:
CompletableFuture<String> cf=CompletableFuture.supplyAsync(异步执行实例);
cf.thenAccept(获得结果后的操作);
cf.exceptionally(发生异常时的操作);
-------------------------------------------------------------
多个CompletableFuture可以串行执行:
CompletableFuture<String> cf1=CompletableFuture.supplyAsync(异步执行实例1);
CompletableFuture<LocalDate> cf2=cf1.thenApplyAsync(异步执行实例2);
CompletableFuture<Float> cf3=cf2.thenApplyAsync(异步执行实例3);
cf3.thenAccept(实例3获得结果后的操作);
-----------------------------------------------
CompletableFuture的命名规则:
xxx():继续在已有的线程中执行
xxxAsync():用Executor的新线程执行
-----------------------------------------------
总结
CompletableFuture对象可以指定异步处理流程:
。thenAccept()处理正常结果
。exceptional()处理异常结果
。thenApplyAsync() 用于串行化另一个CompletableFuture
。anyOf/allOf用于并行化两个CompletableFuture
*/
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.Callable;
public class DownloadUtil implements Callable<String> {
String url;
public DownloadUtil(String url) {
this.url = url;
}
@Override
public String call() throws Exception {
System.out.println("Start download "+url+"...");
URLConnection conn =new URL(this.url).openConnection();
conn.connect();
try(BufferedReader reader=new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"))){
String s=null;
StringBuilder sb=new StringBuilder();
while ((s=reader.readLine())!=null){
sb.append(s).append("\n");
}
return sb.toString();
}
}
public static String download(String url) throws Exception {
DownloadUtil dlu=new DownloadUtil(url);
return dlu.call();
}
}
(16)Fork/Join
/*
Fork/Join线程池可以执行一种特殊的任务:
把一个大任务拆成多个小任务并行执行
JDK1.7引入
*/
public class ForkJoinDemo {
public static void main(String[] args) {
long[] array=new long[100000000];
long expectedSum=0;
for (int i = 0; i < array.length; i++) {
array[i]= (long) (Math.random()*100);
}
System.out.println("Expected sum:"+expectedSum);
//fork-join
ForkJoinTask<Long> task=new Sumtask(array,0,array.length);
long startTime=System.currentTimeMillis();
Long result= ForkJoinPool.commonPool().invoke(task);
long endtime=System.currentTimeMillis();
System.out.println("fork-join sum:"+result+" in "+(endtime-startTime));
}
}
class Sumtask extends RecursiveTask<Long> {
static final int THRESHOLD = 5000;
long[] array;
int start;
int end;
public Sumtask(long[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if (end - start <= THRESHOLD) {
//如果任务足够小,直接计算;
long sum = 0;
for (int i = start; i < end; i++) {
sum += this.array[i];
// try {
// Thread.sleep(2);
// } catch (InterruptedException e) {
//
// }
}
return sum;
}
//任务太大,一分为二;
int middle = (end + start) / 2;
Sumtask subtask1=new Sumtask(this.array,start,middle);
Sumtask subtask2=new Sumtask(this.array,middle,end);
invokeAll(subtask1,subtask2);
Long subresult1=subtask1.join();
Long subresult2=subtask2.join();
Long result=subresult1+subresult2;
System.out.println("result="+subresult1+"+"+subresult2);
return result;
}
}
/*
Fork-Join是一种基于“分治”的算法。
分解任务+合并结果
ForkJoinPool线程池可以把一个大任务分拆成小任务并行执行
任务类必须继承自RecursiveTask-RecursiveAction
使用Fork-Join模式可以进行并行计算提高效率
*/