线程概念
创建新线程
- 从Thread派生;
- 实现Runnable接口(不需要返回结果);
- 通过 Callable 和 Future 创建线程(需要返回结果);
必须调用start()
方法才能启动新线程
Thread.sleep()
可以把当前线程暂停一段时间
线程的状态
一个线程对象只能调用一次start()
Java线程对象Thread的状态包括:
New / Runnable / Blocked / Waiting / Timed Waiting / Terminated
一个线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
下图显示了一个线程完整的生命周期。(图片引用自java菜鸟教程)
通过对另一个线程对象调用join()方法可以等待其执行结束
可以指定等待时间,超过等待时间线程仍然没有结束就不再等待
对已经运行结束的线程调用join()方法会立刻返回
join练习
小明设计了一个多线程程序,希望创建3个线程,每个线程先打印
Hello, xxx!
然后等待1秒,再打印:
Goodbye, xxx!
class HelloThread extends Thread {
String name;
public HelloThread(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("Hello, " + name + "!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("Goodbye, " + name + "!");
}
}
public class Main {
public static void main(String[] args) throws Exception {
List<Thread> threads = new ArrayList<>();
for (String name : Arrays.asList("Bob", "Alice", "Tom")) {
threads.add(new HelloThread(name));
}
System.out.println("START");
for (Thread t : threads) {
t.start();
t.join();
}
System.out.println("END");
}
}
小明期望的输出是:
START
Hello, Bob!
Hello, Alice!
Hello, Tom!
(等待约1秒)
Goodbye, Bob!
Goodbye, Alice!
Goodbye, Tom!
END
但实际输出却是:
START
Hello, Bob!
(等待约1秒)
Goodbye, Bob!
Hello, Alice!
(等待约1秒)
Goodbye, Alice!
Hello, Tom!
(等待约1秒)
Goodbye, Tom!
END
请帮他找找问题原因,并修复。
中断线程
应用场景:文件下载的取消操作
调用interrupt()方法可以中断一个线程
通过检测isInterrupted()标志获取当前线程是否已中断
如果线程处于等待状态,该线程会捕获InterruptedException
isInterrupted()为true或者捕获了InterruptedException都应该立刻结束
class HelloThread extends Thread {
@Override
public void run() {
while (!isInterrupted()) {
System.out.println("Hello!");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println("Interrupted!");
break;
}
}
System.out.println("Thread end");
}
}
public class Main {
public static void main(String[] args) throws Exception {
HelloThread t = new HelloThread();
t.start();
Thread.sleep(1000);
t.interrupt();
System.out.println("Main end");
}
}
通过标志位判断时需要正确使用volatile关键字
volatile关键字解决了共享变量在线程间的可见性问题
守护线程
守护线程是为其他线程服务的线程
所有非守护线程都执行完毕后,虚拟机退出(没人管守护线程的死活)
守护线程不能持有资源(如打开文件等)
创建守护线程:
setDaemon(true)
例子:
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
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) {
break;
}
}
}
}
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("Main start");
TimerThread t = new TimerThread();
t.setDaemon(true); // 设置为守护线程
t.start();
Thread.sleep(5000);
System.out.println("Main end");
}
}
举例, GC垃圾回收线程:就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。
应用场景:(1)来为其它线程提供服务支持的情况;(2) 或者在任何情况下,程序结束时,这个线程必须正常且立刻关闭,就可以作为守护线程来使用;反之,如果一个正在执行某个操作的线程必须要正确地关闭掉否则就会出现不好的后果的话,那么这个线程就不能是守护线程,而是用户线程。通常都是些关键的事务,比方说,数据库录入或者更新,这些操作都是不能中断的。
线程同步
线程同步
多线程同时修改变量,会造成逻辑错误
需要通过synchronized同步
同步的本质就是给指定对象加锁
- 注意加锁对象必须是同一个实例
对JVM定义的单个原子操作不需要同步
class AddThread extends Thread {
public void run() {
for (int i = 0; i < Main.LOOP; i++) {
// synchronized(Main.LOCK){
Main.count += 1;
// }
}
}
}
class DecThread extends Thread {
public void run() {
for (int i = 0; i < Main.LOOP; i++) {
// synchronized(Main.LOCK){
Main.count -= 1;
// }
}
}
}
public class Main {
final static int LOOP = 100;
// public static final Object LOCK = new Object(); // 锁住的对象
public static int count = 0;
public static void main(String[] args) throws Exception {
Thread t1 = new AddThread();
Thread t2 = new DecThread();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
synchronized方法
用synchronized修饰方法可以把整个方法变为同步代码块
- synchronized方法加锁对象是this
通过合理的设计和数据封装可以让一个类变为“线程安全”
一个类没有特殊说明,默认不是thread-safe
多线程能否访问某个非线程安全的实例,需要具体问题具体分析
例子:
class Counter {
int count = 0;
public synchronized void add(int n) {
count += n;
}
public synchronized void dec(int n) {
count -= n;
}
public int get() {
return count;
}
}
class AddThread extends Thread {
Counter counter;
public AddThread(Counter counter) {
this.counter = counter;
}
public void run() {
for (int i = 0; i < Main.LOOP; i++) {
counter.add(1);
}
}
}
class DecThread extends Thread {
Counter counter;
public DecThread(Counter counter) {
this.counter = counter;
}
public void run() {
for (int i = 0; i < Main.LOOP; i++) {
counter.dec(1);
}
}
}
public class Main {
final static int LOOP = 10000;
public static void main(String[] args) throws Exception {
Counter counter = new Counter();
Thread t1 = new AddThread(counter);
Thread t2 = new DecThread(counter);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.get());
}
}
死锁
死锁产生的条件:
多线程各自持有不同的锁,并互相试图获取对方已持有的锁,双方无限等待下去:导致死锁
如何避免死锁:
多线程获取锁的顺序要一致
wait / notify
wait / notify用于多线程协调运行:
在synchronized内部可以调用wait()使线程进入等待状态
- 必须在已获得的锁对象上调用wait()方法
在synchronized内部可以调用notify / notifyAll()唤醒其他等待线程
- 必须在已获得的锁对象上调用notify / notifyAll()方法
例子:
import java.util.LinkedList;
import java.util.Queue;
class TaskQueue {
final Queue<String> queue = new LinkedList<>();
public synchronized String getTask() throws InterruptedException {
while (this.queue.isEmpty()) {
this.wait();
}
return queue.remove();
}
public synchronized void addTask(String name) {
this.queue.add(name);
this.notifyAll();
}
}
class WorkerThread extends Thread {
TaskQueue taskQueue;
public WorkerThread(TaskQueue taskQueue) {
this.taskQueue = taskQueue;
}
public void run() {
while (!isInterrupted()) {
String name;
try {
name = taskQueue.getTask();
} catch (InterruptedException e) {
break;
}
String result = "Hello, " + name + "!";
System.out.println(result);
}
}
}
public class Main {
public static void main(String[] args) throws Exception {
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");
}
}
高级concurrent包
ReentrantLock
ReentrantLock可以替代synchronized
ReentrantLock获取锁更安全
必须使用try … finally
保证正确获取和释放锁
tryLock()
可指定超时
例子:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private Lock lock = new ReentrantLock(); // 获取一个lock对象
private int value = 0;
public void add(int m) {
lock.lock();
try {
this.value += m;
} finally {
lock.unlock(); // 保证锁的释放
}
}
public void dec(int m) {
lock.lock();
try {
this.value -= m;
} finally {
lock.unlock();
}
}
public int get() {
lock.lock();
try {
return this.value;
} finally {
lock.unlock();
}
}
}
public class Main {
final static int LOOP = 100;
public static void main(String[] args) throws Exception {
Counter counter = new Counter();
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < LOOP; i++) {
counter.add(1);
}
}
};
Thread t2 = new Thread() {
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());
}
}
ReadWriteLock
使用ReadWriteLock可以提高读取效率:
- ReadWriteLock只允许一个线程写入
- ReadWriteLock允许多个线程同时读取
- ReadWriteLock适合读多写少的场景
例子:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Counter {
private ReadWriteLock lock = new ReentrantReadWriteLock(); // 获取ReentrantReadWriteLock实例
private Lock rlock = lock.readLock(); // 读锁
private Lock wlock = lock.writeLock(); // 写锁
private int value = 0;
public void add(int m) {
wlock.lock();
try {
this.value += m;
} finally {
wlock.unlock();
}
}
public void dec(int m) {
wlock.lock();
try {
this.value -= m;
} finally {
wlock.unlock();
}
}
public int get() {
rlock.lock();
try {
return this.value;
} finally {
rlock.unlock();
}
}
}
public class Main {
final static int LOOP = 100;
public static void main(String[] args) throws Exception {
Counter counter = new Counter();
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < LOOP; i++) {
counter.add(1);
}
}
};
Thread t2 = new Thread() {
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());
}
}
Condition
Condition可以替代wait / notify
Condition对象必须从ReentrantLock对象获取
ReentrantLock+Condition可以替代synchronized + 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;
class TaskQueue {
final Queue<String> queue = new LinkedList<>();
final Lock lock = new ReentrantLock();
final Condition notEmpty = lock.newCondition(); // 获取Condition对象
public String getTask() throws InterruptedException {
lock.lock();
try {
while (this.queue.isEmpty()) {
notEmpty.await();
}
return queue.remove();
} finally {
lock.unlock();
}
}
public void addTask(String name) {
lock.lock();
try {
this.queue.add(name);
notEmpty.signalAll();
} finally {
lock.unlock();
}
}
}
class WorkerThread extends Thread {
TaskQueue taskQueue;
public WorkerThread(TaskQueue taskQueue) {
this.taskQueue = taskQueue;
}
public void run() {
while (!isInterrupted()) {
String name;
try {
name = taskQueue.getTask();
} catch (InterruptedException e) {
break;
}
String result = "Hello, " + name + "!";
System.out.println(result);
}
}
}
public class Main {
public static void main(String[] args) throws Exception {
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");
}
}
concurrent集合
使用java.util.concurrent提供的Blocking集合可以简化多线程编程:
- CopyOnWriteArrayList
- ConcurrentHashMap
- CopyOnWriteArraySet
- ArrayBlockingQueue
- LinkedBlockingQueue
- LinkedBlockingDeque
接口 | 非线程安全 | 线程安全 |
List | ArrayList | CopyOnWriteArrayList |
Map | HashMap | ConcurrentHashMap |
Set | HashSet、TreeSet | CopyOnWriteArraySet |
Queue | ArrayDeque、LinkedList | ArrayBlockingQueue、LinkedBlockingQueue |
Deque | ArrayDeque、LinkedList | LinkedBlockingDeque |
多线程同时访问Blocking集合是安全的
尽量使用JDK提供的concurrent集合,避免自己编写同步代码
java.util.Collections工具还提供了线程安全集合转换工具方法:(性能一般,不如直接用JDK提供的concurrent集合)
Map unsafeMap = new HashMap();
Map safeMap = Collections.synchronizedMap(unsafeMap );
例子:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class WorkerThread extends Thread {
BlockingQueue<String> taskQueue;
public WorkerThread(BlockingQueue<String> taskQueue) {
this.taskQueue = taskQueue;
}
public void run() {
while (!isInterrupted()) {
String name;
try {
name = taskQueue.take();
} catch (InterruptedException e) {
break;
}
String result = "Hello, " + name + "!";
System.out.println(result);
}
}
}
public class Main {
public static void main(String[] args) throws Exception {
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("END");
}
}
Atomic
使用java.util.atomic提供的原子操作可以简化多线程编程:
AtomicInteger/AtomicLong/AtomicIntegerArray等
原子操作实现了无锁的线程安全
适用于计数器,累加器等
例子:
import java.util.concurrent.atomic.AtomicInteger;
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();
}
}
public class Main {
final static int LOOP = 100;
public static void main(String[] args) throws Exception {
Counter counter = new Counter();
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < LOOP; i++) {
counter.add(1);
}
}
};
Thread t2 = new Thread() {
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());
}
}
ExecutorService
JDK提供了ExecutorService实现了线程池功能
线程池内部维护一组线程,可以高效执行大量小任务
Executors提供了静态方法创建不同类型的ExecutorService
常用ExecutorService:
- FixedThreadPool:线程数固定
- CachedThreadPool:线程数根据任务动态调整
- SingleThreadExecutor:仅单线程执行
必须调用shutdown()关闭ExecutorService;
例子:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class PrintTask implements Runnable {
String name;
public PrintTask(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Hello, " + name + "!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
public class ThreadPool {
public static void main(String[] args) throws Exception {
// 初始化线程数为3的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 初始化一个单线程的线程池
// ExecutorService executor = Executors.newSingleThreadExecutor();
// 初始化一个线程数根据任务动态调整的线程池
// ExecutorService executor = Executors.newCachedThreadPool();
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();
}
}
ScheduledThreadPool可以定期调度多个任务(可取代Timer)
两种执行模式:
例子:
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
class HelloTask implements Runnable {
String name;
public HelloTask(String name) {
this.name = name;
}
public void run() {
System.out.println("Hello, " + name + "! It is " + LocalTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("Goodbye, " + name + "! It is " + LocalTime.now());
}
}
public class Schedule {
public static void main(String[] args) throws Exception {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
executor.scheduleAtFixedRate(new HelloTask("Bob"), 2, 5, TimeUnit.SECONDS);
executor.scheduleWithFixedDelay(new HelloTask("Alice"), 2, 5, TimeUnit.SECONDS);
}
}
Future
Future表示一个未来可能会返回的结果
提交Callable任务,可以获得一个Future对象
可以用Future在将来某个时刻获取结果
例子:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class DownloadTask implements Callable<String> {
String url;
public DownloadTask(String url) {
this.url = url;
}
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 class Main {
public static void main(String[] args) throws Exception {
// 初始化一个线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 创建一个下载任务
DownloadTask task = new DownloadTask("http://www.sina.com.cn/");
// 提交给线程池执行,返回一个future
Future<String> future = executor.submit(task);
// 获取结果,可能阻塞
String html = future.get();
// 另外一种获取结果的方式
// while(!future.isDone()){
// Thread.sleep(100);
// }
// String html = future.get();
System.out.println(html);
executor.shutdown();
}
}
CompletableFuture
CompletableFuture的优点:
- 异步任务结束时,会自动回调某个对象的方法
- 异步任务出错时,会自动回调某个对象的方法
- 主线程设置好回调后,不再关心异步任务的执行
CompletableFuture对象可以指定异步处理流程:
- thenAccept( ):处理正常结果
- exceptional( ):处理异常结果
- thenApplyAsync( ) :用于串行化另一个CompletableFuture
- anyOf / allOf :用于并行化两个CompletableFuture
例子:
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
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) {
throw new RuntimeException(e);
}
}
}
public class CompletableFutureSample {
public static void main(String[] args) throws Exception {
// 创建CompletableFuture实例
CompletableFuture<Float> getStockFuture = CompletableFuture.supplyAsync(new StockSupplier());
// 执行成功后的回调方法
getStockFuture.thenAccept(new Consumer<Float>() {
@Override
public void accept(Float price) {
System.out.println("Current price: " + price);
}
});
// 抛出异常后的回调方法
getStockFuture.exceptionally(new Function<Throwable, Float>() {
@Override
public Float apply(Throwable t) {
System.out.println("Error: " + t.getMessage());
return Float.NaN;
}
});
// 等待线程结束
getStockFuture.join();
}
}
串行例子:
import java.net.URLEncoder;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
class Price {
final String code;
final float price;
Price(String code, float price) {
this.code = code;
this.price = price;
}
}
class StockLookupSupplier implements Supplier<String> {
String name;
public StockLookupSupplier(String name) {
this.name = name;
}
public String get() {
System.out.println("lookup: " + name);
try {
String url = "http://suggest3.sinajs.cn/suggest/type=11,12&key=" + URLEncoder.encode(name, "UTF-8");
String result = DownloadUtil.download(url);
String[] ss = result.split(",");
return ss[3];
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class CompletableFutureSequenceSample {
public static void main(String[] args) throws Exception {
String name = "上证指数";
// 查询证券代码
CompletableFuture<String> getStockCodeFuture = CompletableFuture.supplyAsync(new StockLookupSupplier(name));
// 查询证券价格
CompletableFuture<Price> getStockPriceFuture = getStockCodeFuture.thenApplyAsync(new Function<String, Price>() {
public Price apply(String code) {
System.out.println("got code: " + code);
try {
String url = "http://hq.sinajs.cn/list=" + code;
String result = DownloadUtil.download(url);
String[] ss = result.split(",");
return new Price(code, Float.parseFloat(ss[3]));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
// 显示最终结果
getStockPriceFuture.thenAccept(new Consumer<Price>() {
public void accept(Price p) {
System.out.println(p.code + ": " + p.price);
}
});
// 等待线程执行完毕
getStockPriceFuture.join();
}
}
多个CompletableFuture也可以并行执行
并行例子:
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Supplier;
class StockPrice {
final float price;
final String from;
StockPrice(float price, String from) {
this.price = price;
this.from = from;
}
public String toString() {
return "Price: " + price + " from " + from;
}
}
class StockFromSina implements Supplier<StockPrice> {
@Override
public StockPrice 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 new StockPrice(Float.parseFloat(ss[3]), "sina");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class StockFromNetease implements Supplier<StockPrice> {
@Override
public StockPrice get() {
String url = "http://api.money.126.net/data/feed/0000001,money.api";
System.out.println("GET: " + url);
try {
String result = DownloadUtil.download(url);
int priceIndex = result.indexOf("\"price\"");
int start = result.indexOf(":", priceIndex);
int end = result.indexOf(",", priceIndex);
return new StockPrice(Float.parseFloat(result.substring(start + 1, end)), "netease");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class CompletableFutureAnyOfSample {
public static void main(String[] args) throws Exception {
// 从新浪获取证券价格
CompletableFuture<StockPrice> getStockFromSina = CompletableFuture.supplyAsync(new StockFromSina());
// 从网易获取证券价格
CompletableFuture<StockPrice> getStockFromNetease = CompletableFuture.supplyAsync(new StockFromNetease());
// 把上面两个CompletableFuture实例合成一个并行的实例,有一个返回结果,就调用thenAccept方法
CompletableFuture<Object> getStock = CompletableFuture.anyOf(getStockFromSina, getStockFromNetease);
// 把上面两个CompletableFuture实例合成一个并行的实例,两个都返回结果,才会调用thenAccept方法
// CompletableFuture<Void> getStock = CompletableFuture.allOf(getStockFromSina, getStockFromNetease);
// 获取结果,两个CompletableFuture,显示先返回结果的那个。
getStock.thenAccept(new Consumer<Object>() {
public void accept(Object result) {
System.out.println("Reuslt: " + result);
}
});
// 等待线程执行完毕
getStock.join();
}
}
工具类:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class DownloadUtil {
public static String download(String url) throws IOException {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setDoOutput(false);
conn.setAllowUserInteraction(false);
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);
conn.connect();
ByteArrayOutputStream output = new ByteArrayOutputStream(10240);
try (InputStream input = conn.getInputStream()) {
int n;
byte[] buffer = new byte[2048];
while ((n = input.read(buffer)) != (-1)) {
output.write(buffer, 0, n);
}
}
conn.disconnect();
return new String(output.toByteArray(), "UTF-8");
}
}
Fork/Join
Fork/Join是一种基于“分治”的算法:分解任务+合并结果
ForkJoinPool线程池可以把一个大任务分拆成小任务并行执行
任务类必须继承自RecursiveTask(有返回值)/RecursiveAction(没有返回值),并复写compute方法
使用Fork/Join模式可以进行并行计算提高效率
例子:
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
class SumTask extends RecursiveTask<Long> {
// 指定如何分解任务
static final int THRESHOLD = 500;
long[] array;
int start;
int end;
SumTask(long[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
// 复写compute方法
@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;
System.out.println(String.format("split %d~%d ==> %d~%d, %d~%d", start, end, start, middle, middle, end));
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 + " ==> " + result);
return result;
}
}
public class ForkJoinTaskSample {
public static void main(String[] args) throws Exception {
// 创建1000个随机数组成的数组:
long[] array = new long[1000];
long expectedSum = 0;
for (int i = 0; i < array.length; i++) {
array[i] = random();
expectedSum += array[i];
}
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) + " ms.");
}
static Random random = new Random(0);
static long random() {
return random.nextInt(10000);
}
}
线程工具类
调用Thread.currentThread()获取当前线程。
JDK提供了ThreadLocal,在一个线程中传递同一个对象。
ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的。
ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递)
使用ThreadLocal要用try … finally结构。
例子:
class User {
String name;
int level;
public User(String name, int level) {
this.name = name;
this.level = level;
}
}
class UserContext implements AutoCloseable {
// 全局唯一静态变量:
static final ThreadLocal<User> context = new ThreadLocal<>();
// 获取当前线程的ThreadLocal User:
public static User getCurrentUser() {
return context.get();
}
// 初始化ThreadLocal的User:
public UserContext(User user) {
context.set(user);
}
// 移除ThreadLocal关联的User:
public void close() {
context.remove();
}
}
class ProcessThread extends Thread {
User user;
ProcessThread(User user) {
this.user = user;
}
public void run() {
try (UserContext ctx = new UserContext(user)) {
// step 1:
new Greeting().hello();
// step 2:
Level.checkLevel();
// step 3:
// TODO:
} finally{
ctx.close();
}
}
}
class Greeting {
void hello() {
User user = UserContext.getCurrentUser();
System.out.println("Hello, " + user.name + "!");
}
}
class Level {
static void checkLevel() {
User user = UserContext.getCurrentUser();
if (user.level > 100) {
System.out.println(user.name + " is a VIP user.");
} else {
System.out.println(user.name + " is a registered user.");
}
}
}
public class Main {
public static void main(String[] args) throws Exception {
Thread t1 = new ProcessThread(new User("Bob", 120));
Thread t2 = new ProcessThread(new User("Alice", 98));
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Main end");
}
}