多线程 - JUC
Java 的多线程编程主要依靠 java.util.concurrent 包中的类和接口,常用的多线程类如下:
Thread:
Java中的Thread类允许在单个程序中同时运行多个线程。Thread类提供了一种实现并发编程的方式,以使程序能够更有效地利用多核处理器和多线程处理。以下是Thread类的一些重要概念和用法:
创建线程:创建一个新的线程需要创建Thread类的一个实例,并调用该实例的start()方法。这将启动一个新的线程并调用线程的run()方法。
继承Thread类:可以通过继承Thread类来创建自定义线程类。继承Thread类的子类必须实现run()方法,该方法定义了线程的主体。
实现Runnable接口:可以通过实现Runnable接口来创建线程类。实现Runnable接口的类必须实现run()方法,并且可以将该类的实例传递给Thread类的构造函数。
线程状态:线程可以处于不同的状态,包括新建、就绪、运行、阻塞和死亡等。可以使用Thread类的getState()方法来获取线程的状态。
线程优先级:可以使用Thread类的setPriority()和getPriority()方法来设置和获取线程的优先级。线程的优先级越高,它就越有可能被执行。
线程同步:当多个线程访问共享资源时,需要使用线程同步来确保线程之间的协调和正确性。可以使用synchronized关键字来实现线程同步。
线程间通信:线程间通信是指多个线程之间通过共享变量进行数据传输和协调。可以使用wait()、notify()和notifyAll()等方法来实现线程间通信。
/**
* Java 程序中的线程是通过 Thread 类来创建和启动的,这个类提供了一些方法来控制线程的状态和行为。
*/
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread 启动!");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
Runnable:
为了创建一个线程,可以实现Runnable接口并重写run()方法,然后将该对象传递给Thread类的构造函数中。Thread类将使用该Runnable对象作为线程的执行代码。
/**
* Java 中的线程可以通过实现 Runnable 接口来创建,这个接口只有一个 run() 方法,表示线程要执行的代码。
* @author Administrator
*/
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("MyRunnable 启动!");
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
Callable和Future:
Callable 接口也可以创建线程,和 Runnable 接口不同的是,Callable 接口中的 call() 方法可以返回一个结果,这个结果可以用 Future 接口来获取。
Future 接口表示一个异步计算的结果,可以用来检查计算是否完成、等待计算完成、获取计算结果等操作。
/**
* Callable 接口也可以创建线程,和 Runnable 接口不同的是,Callable 接口中的 call() 方法可以返回一个结果,这个结果可以用 Future 接口来获取。
* @author Administrator
*/
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
}
return sum;
}
/**
* Executors.newSingleThreadExecutor()
* 手动创建线程池,效果会更好哦。
* Inspection info:
* 线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,
* 这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
*
* 说明:Executors返回的线程池对象的弊端如下:
* 1)FixedThreadPool 和 SingleThreadPool:
* 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
* 2)CachedThreadPool:
* 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
*
* Positive example 1:
* //org.apache.commons.lang3.concurrent.BasicThreadFactory
* ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
* new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
*
*
*
* Positive example 2:
* ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
* .setNameFormat("demo-pool-%d").build();
*
* //Common Thread Pool
* ExecutorService pool = new ThreadPoolExecutor(5, 200,
* 0L, TimeUnit.MILLISECONDS,
* new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
*
* pool.execute(()-> System.out.println(Thread.currentThread().getName()));
* pool.shutdown();//gracefully shutdown
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
MyCallable callable = new MyCallable();
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Integer> future = executorService.submit(callable);
Integer result = future.get();
// 使用isDone()方法检查结果是否已经准备好
while (!future.isDone()) {
System.out.println("Future 还未执行完毕或还未返回结果!");
Thread.sleep(500);
}
// 使用get(long timeout, TimeUnit unit)方法设置一个超时时间
Integer result1 = future.get(1500, TimeUnit.MILLISECONDS);
System.out.println(result1);
System.out.println(result);
executorService.shutdown();
}
}
Future.get()方法是阻塞的。它会一直等待,直到该Future对象的结果准备就绪并返回。
如果想要非阻塞地获取结果,可以使用isDone()方法来检查结果是否已经准备好,或者使用get(long timeout, TimeUnit unit)方法来设置一个超时时间,如果在超时时间内结果还没有准备好,将抛出一个TimeoutException异常。
此外,Java中还有CompletionStage和CompletableFuture接口,它们提供了更加灵活和强大的异步编程模型,支持非阻塞式的等待和结果处理。使用这些接口可以更方便地进行并发编程,提高程序的性能和可维护性。
CompletionStage
public class MyCompletionStage {
public static void main(String[] args) {
// 创建一个CompletionStage对象
CompletionStage<Integer> stage1 = CompletableFuture.supplyAsync(() -> {
System.out.println("Task 1 执行");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 10;
});
// 使用thenCompose方法将两个CompletionStage组合起来
CompletionStage<Integer> stage2 = stage1.thenComposeAsync(result1 -> {
System.out.println("Task 2 执行,当前结果是 " + result1);
return CompletableFuture.supplyAsync(() -> result1 * 2);
});
// 使用thenAccept方法处理最终结果
stage2.thenAcceptAsync(result2 -> {
System.out.println("最终结果是: " + result2);
});
// 等待所有任务完成
try {
//
System.out.println("sleep(5000)是为了等待所有任务完成,可以得知使用了thenComposeAsync和thenAcceptAsync方法来异步执行任务。" +
"这些方法会将任务提交到线程池中执行,并立即返回,不会阻塞当前线程。" +
"如果我们使用thenCompose和thenAccept方法,那么这些方法将会在当前线程中执行,可能会阻塞线程,影响程序的性能。");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ThreadPoolExecutor:
线程池是 Java 并发编程中的重要概念,可以通过 ThreadPoolExecutor 类来创建一个线程池,线程池可以管理一组线程,复用线程并控制并发执行的线程数量。
/**
* 线程池是 Java 并发编程中的重要概念,可以通过 ThreadPoolExecutor 类来创建一个线程池,线程池可以管理一组线程,复用线程并控制并发执行的线程数量。
* @author Administrator
*/
public class MyThreadPoolExecutor implements Runnable {
private int taskNum;
public MyThreadPoolExecutor(int num) {
this.taskNum = num;
}
@Override
public void run() {
System.out.println("任务 " + taskNum + " 开始!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务 " + taskNum + " 完成!");
}
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
for (int i = 1; i <= 10; i++) {
MyThreadPoolExecutor task = new MyThreadPoolExecutor(i);
executor.execute(task);
}
executor.shutdown();
}
}
Semaphore:
Semaphore 类是一个计数信号量,用于控制同时访问某个资源的线程数量,可以实现类似于控制并发数的功能。
/**
* Semaphore 类是一个计数信号量,用于控制同时访问某个资源的线程数量,可以实现类似于控制并发数的功能。(最大并发数,可重复利用也是和CountDownLatch的区别)
*/
public class MySemaphore extends Thread {
private Semaphore semaphore;
private int num;
public MySemaphore(Semaphore semaphore, int num) {
this.semaphore = semaphore;
this.num = num;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("Thread " + num + " 执行");
Thread.sleep(1000);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 5; i++) {
MySemaphore thread = new MySemaphore(semaphore, i);
thread.start();
}
}
}
CountDownLatch:
CountDownLatch 类用于等待某个或某些线程执行完后再执行另外的线程,可以实现类似于多线程协同的功能。
/**
* 创建了一个CountDownLatch对象,它的计数器初始值为3。
* 然后我们创建了三个WorkerThread线程,并将CountDownLatch对象传递给它们。
* 每个线程都会休眠一段随机时间,模拟完成任务所需的时间。
* 线程完成任务后,它们会调用CountDownLatch的countDown()方法,将计数器减1。
* 主线程调用await()方法等待所有线程完成任务。
* 当计数器减为0时,所有线程已经完成任务,主线程继续执行,并输出提示信息。
* @author: lfsun
*/
public class MyCountDownLatch extends Thread {
public static void main(String[] args) throws InterruptedException {
// 创建CountDownLatch对象并设置初始计数器为3
CountDownLatch latch = new CountDownLatch(3);
// 创建三个线程并启动它们
new WorkerThread(latch, "WorkerThread-1").start();
new WorkerThread(latch, "WorkerThread-2").start();
new WorkerThread(latch, "WorkerThread-3").start();
// 等待所有线程完成任务
latch.await();
// 所有线程完成任务后,输出提示信息
System.out.println("所有 worker 线程完成!");
}
// 自定义线程类
static class WorkerThread extends Thread {
private CountDownLatch latch;
public WorkerThread(CountDownLatch latch, String name) {
super(name);
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println(getName() + " 开始工作!");
// 线程休眠一段时间,模拟完成任务所需的时间
Thread.sleep(1000);
System.out.println(getName() + " 完成工作!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 计数器减 1
latch.countDown();
}
}
}
}
CyclicBarrier:
CyclicBarrier 类用于让一组线程互相等待,直到所有线程都到达某个屏障点后才继续执行,可以实现类似于多线程同步的功能。
/**
* @author: lfsun
*/
public class MyCyclicBarrier extends Thread {
private CyclicBarrier barrier;
private int num;
public MyCyclicBarrier(CyclicBarrier barrier, int num) {
this.barrier = barrier;
this.num = num;
}
@Override
public void run() {
try {
System.out.println("Thread " + num + " 在等着!");
barrier.await();
System.out.println("Thread " + num + " 在执行!");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有线程都准备!"));
for (int i = 1; i <= 3; i++) {
MyCyclicBarrier thread = new MyCyclicBarrier(barrier, i);
thread.start();
}
}
}
Lock 和 Condition:
Lock 接口及其实现类用于替代传统的 synchronized 关键字,提供更灵活的锁定机制,可以实现公平锁、非公平锁、可重入锁、读写锁等多种类型的锁。Condition 接口及其实现类则用于替代传统的 Object.wait() 和 Object.notify() 方法,可以实现更精细的线程等待和唤醒机制。
/**
* @author: lfsun
*/
public class MyCondition extends Thread{
private Lock lock;
private Condition condition;
private int num;
public MyCondition(Lock lock, Condition condition, int num) {
this.lock = lock;
this.condition = condition;
this.num = num;
}
@Override
public void run() {
lock.lock();
try {
System.out.println("Thread " + num + " 在等着!");
condition.await();
System.out.println("Thread " + num + " 在执行!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
for (int i = 1; i <= 3; i++) {
MyCondition thread = new MyCondition(lock, condition, i);
thread.start();
}
Thread.sleep(1000);
lock.lock();
try {
condition.signalAll();
} finally {
lock.unlock();
}
}
}
Atomic 原子类:
Java 中的 Atomic 原子类可以提供线程安全的自增、自减、比较交换等操作,可以在一些需要高效并发访问的场合中使用。
/**
* @author Administrator
*/
public class MyAtomicInteger {
// 创建一个原子整数,初始值为0,用于计数(默认也是 0)
// private static AtomicInteger counter = new AtomicInteger();
private static AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
// 创建两个IncrementTask线程实例
Thread thread1 = new Thread(new IncrementTask());
Thread thread2 = new Thread(new IncrementTask());
// 启动线程
thread1.start();
thread2.start();
try {
// 等待线程执行完毕
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出计数器的最终值
System.out.println("计算后结果: " + counter.get());
}
/**
* 定义一个实现Runnable接口的IncrementTask类,用于增加计数器的值
*/
static class IncrementTask implements Runnable {
@Override
public void run() {
// 对计数器进行100000次增加操作
for (int i = 0; i < 100000; i++) {
// 使用incrementAndGet()方法实现原子增加
counter.incrementAndGet();
}
}
}
}
ReentrantLock:
ReentrantLock是Java中提供的一种锁定机制,用于在多线程环境中保护共享资源的访问。它是Lock接口的实现,允许线程获取和释放对共享资源的锁定。
相较于Java中传统的synchronized关键字,ReentrantLock提供了更大的灵活性和控制能力。具体而言,它允许:
可重入锁定:已经获得了某个资源的锁的线程可以再次获取同一个锁,而不会导致死锁。
公平性:它提供了使用公平的顺序策略的选项,在此情况下,锁将被授予等待时间最长的线程。
条件变量:它提供了Condition接口的实现,用于实现线程等待某个条件满足后再继续执行的机制。
与synchronized关键字不同,ReentrantLock允许以非阻塞方式尝试获取锁,因此它可以避免死锁。此外,ReentrantLock还提供了可中断锁定、超时锁定等功能,使得它在一些特定的场景下更为灵活和实用。
/**
* @author: lfsun
* @time: 8:53
* @date: 2023/4/2
*/
public class MyReentrantLock extends Thread{
private ReentrantLock lock;
private int num;
public MyReentrantLock(ReentrantLock lock, int num) {
this.lock = lock;
this.num = num;
}
/**
* 锁【lock.lock】必须紧跟try代码块,且unlock要放到finally第一行。
* Inspection info:
* 在使用阻塞等待获取锁的方式中,必须在try代码块之外,并且在加锁方法与try代码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在finally中无法解锁。
* 说明一:如果在lock方法与try代码块之间的方法调用抛出异常,那么无法解锁,造成其它线程无法成功获取锁。
* 说明二:如果lock方法在try代码块之内,可能由于其它方法抛出异常,导致在finally代码块中,unlock对未加锁的对象解锁,它会调用AQS的tryRelease方法(取决于具体实现类),抛出IllegalMonitorStateException异常。
* 说明三:在Lock对象的lock方法实现中可能抛出unchecked异常,产生的后果与说明二相同。 java.concurrent.LockShouldWithTryFinallyRule.rule.desc
*
* Positive example:
* Lock lock = new XxxLock();
* // ...
* lock.lock();
* try {
* doSomething();
* doOthers();
* } finally {
* lock.unlock();
* }
*
*
*
* Negative example:
* Lock lock = new XxxLock();
* // ...
* try {
* // If an exception is thrown here, the finally block is executed directly
* doSomething();
* // The finally block executes regardless of whether the lock is successful or not
* lock.lock();
* doOthers();
*
* } finally {
* lock.unlock();
* }
*/
@Override
public void run() {
lock.lock();
try {
// lock.lock();
System.out.println("Thread " + num + " 在执行!");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
for (int i = 1; i <= 3; i++) {
MyReentrantLock thread = new MyReentrantLock(lock, i);
thread.start();
}
}
}
ReadWriteLock:
ReadWriteLock(读写锁)是Java提供的另一种锁机制,它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。与ReentrantLock不同的是,ReadWriteLock有两种锁:读锁和写锁。
/**
* @author Administrator
*/
public class MyReentrantReadWriteLock {
// 创建一个Map作为缓存
private Map<String, Object> cache = new HashMap<>();
// 创建一个读写锁
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
// 向缓存中添加数据,这是一个写操作
public void put(String key, Object value) {
// 获取写锁
rwLock.writeLock().lock();
try {
// 写操作
cache.put(key, value);
} finally {
// 释放写锁
rwLock.writeLock().unlock();
}
}
// 从缓存中获取数据,这是一个读操作
public Object get(String key) {
// 获取读锁
rwLock.readLock().lock();
try {
// 读操作
return cache.get(key);
} finally {
// 释放读锁
rwLock.readLock().unlock();
}
}
// 清空缓存,这是一个写操作
public void clear() {
// 获取写锁
rwLock.writeLock().lock();
try {
// 写操作
cache.clear();
} finally {
// 释放写锁
rwLock.writeLock().unlock();
}
}
public static void main(String[] args) {
MyReentrantReadWriteLock cache = new MyReentrantReadWriteLock();
// 添加数据到缓存
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.put("key3", "value3");
// 从缓存中获取数据
System.out.println(cache.get("key1"));
System.out.println(cache.get("key2"));
System.out.println(cache.get("key3"));
// 清空缓存
cache.clear();
// 再次从缓存中获取数据
System.out.println(cache.get("key1"));
System.out.println(cache.get("key2"));
System.out.println(cache.get("key3"));
}
}
ConcurrentHashMap:
ConcurrentHashMap是Java中线程安全的哈希表实现,它支持高并发的读写操作,能够在多线程环境下提供更好的性能。
ConcurrentHashMap 1.7、1.8
1.实现方式
在 Java 1.7 中,ConcurrentHashMap 的实现基于分段锁(Segment),将哈希表分成多个段,每个段都有自己的锁,可以支持并发的访问。在 Java 1.8 中,ConcurrentHashMap 的实现基于 CAS(Compare and Swap)操作和 Synchronized,可以更加高效地实现并发操作。
1.大小调整机制
在 Java 1.7 中,ConcurrentHashMap 的大小调整机制是基于 rehashing 的,即需要将原有的数据重新分配到新的桶中,这个过程需要加锁,会影响并发性能。在 Java 1.8 中,ConcurrentHashMap 的大小调整机制是基于扩容和分段锁的,只需要锁定需要扩容的段,可以避免锁定整个哈希表。
1.并发度
在 Java 1.7 中,ConcurrentHashMap 的默认并发度是 16,也就是默认会创建 16 个段。在 Java 1.8 中,默认的并发度是 CPU 核心数的两倍,可以根据实际情况进行调整。
1.数据结构
在 Java 1.7 中,ConcurrentHashMap 的数据结构是数组+链表+分段锁,每个段都有自己的数组和链表,数据的存储方式类似于 HashMap。在 Java 1.8 中,ConcurrentHashMap 的数据结构是数组+链表/红黑树+CAS/Synchronized,当链表长度超过一定阈值时,会将链表转化为红黑树,可以提高查询的效率。
总的来说,Java 1.8 中的 ConcurrentHashMap 相对于 Java 1.7 中的版本在并发性能、调整大小机制等方面有所改进,可以更好地支持高并发的场景。
/**
* @author Administrator
*/
public class MyConcurrentHashMap {
public static void main(String[] args) {
// 创建一个ConcurrentHashMap实例
ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
// 在map中插入一些数据
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
// 输出map的内容
System.out.println("初始的map: " + map);
// 使用putIfAbsent方法插入一个新值,如果key不存在,则插入成功,返回null;否则返回原来的value
String oldValue = map.putIfAbsent(2, "New Two");
System.out.println("插入新值后的map: " + map);
System.out.println("putIfAbsent返回的旧值: " + oldValue);
// 使用replace方法替换一个已有的key对应的value,如果替换成功,则返回true;否则返回false
boolean replaced = map.replace(2, "Two", "New Two");
System.out.println("替换后的map: " + map);
System.out.println("replace返回的布尔值: " + replaced);
// 使用remove方法删除一个key-value对,如果删除成功,则返回true;否则返回false
boolean removed = map.remove(3, "Three");
System.out.println("删除后的map: " + map);
System.out.println("remove返回的布尔值: " + removed);
}
}
ConcurrentLinkedQueue:
ConcurrentLinkedQueue是Java中的一个线程安全的队列实现,它基于链表数据结构实现,可以支持高效的并发访问。
相较于传统的队列实现,ConcurrentLinkedQueue具有以下特点:
线程安全:它的实现方式基于非阻塞算法,可以保证多线程并发访问时的安全性。
高效性:由于使用了无锁算法,避免了线程间的竞争,因此它的性能比基于锁的队列实现更好,特别是在高并发场景下。
支持FIFO操作:它遵循先进先出的原则,可以保证队列中元素的顺序性。
ConcurrentLinkedQueue的实现基于CAS(Compare-and-Swap)算法,通过无锁方式保证元素的添加、删除和查询操作的线程安全性。同时,由于它的实现方式避免了锁的使用,因此在高并发场景下可以获得更好的性能表现。
需要注意的是,由于ConcurrentLinkedQueue不支持阻塞操作,因此在需要阻塞等待队列中元素的场景下,可以考虑使用BlockingQueue接口的实现类,例如ArrayBlockingQueue和LinkedBlockingQueue。
/**
* @author Administrator
*/
public class MyConcurrentLinkedQueue {
public static void main(String[] args) {
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
// 添加元素到队列
queue.offer("element1");
queue.offer("element2");
queue.offer("element3");
System.out.println("队列的初始状态: " + queue);
// 删除队列的头元素
String headElement = queue.poll();
System.out.println("删除的头元素: " + headElement);
System.out.println("队列的状态: " + queue);
// 检查队列的头元素,但不删除
String peekElement = queue.peek();
System.out.println("检查的头元素: " + peekElement);
System.out.println("队列的状态: " + queue);
// 检查队列是否包含指定的元素
boolean isContains = queue.contains("element1");
System.out.println("队列是否包含元素 'element1': " + isContains);
// 清空队列
queue.clear();
System.out.println("队列是否为空:" + queue.isEmpty());
}
}
CompletableFuture :
CompletableFuture是Java 8中新增的一个异步编程工具,它提供了一种函数式编程的方式来处理异步计算结果,同时也可以方便地处理异常情况。
CompletableFuture的主要特点包括:
异步执行:它可以通过supplyAsync()或runAsync()等方法异步执行一个任务,并返回一个CompletableFuture对象,可以通过该对象获取任务执行结果或处理异常情况。
可组合性:通过thenApply()、thenAccept()、thenCompose()、thenCombine()等方法,可以将多个CompletableFuture对象进行组合,实现异步任务的串行、并行执行等复杂逻辑。
回调机制:通过whenComplete()、exceptionally()等方法,可以为CompletableFuture对象添加回调函数,以便在任务执行完成或发生异常时进行处理。
线程池支持:CompletableFuture支持使用指定的线程池执行异步任务,以避免线程资源的浪费。
CompletableFuture可以帮助我们简化异步编程的复杂度,尤其是在处理多个异步任务组合的场景下,它的函数式编程方式使得代码更加清晰易懂。同时,由于它支持回调函数和异常处理等功能,可以更好地处理异步任务执行的结果和异常情况。
/**
* @author Administrator
*/
public class MyCompletableFuture {
public static void main(String[] args) {
// 创建一个CompletableFuture对象,用于异步执行任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000); // 模拟一个耗时的操作
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, CompletableFuture!";
});
// 注册一个回调函数,当任务执行完成时会自动调用
future.thenAccept(result -> {
System.out.println("不一定哪个先执行1:任务执行完成,结果为:" + result);
});
// 阻塞当前线程,等待任务执行完成并获取结果
try {
String result = future.get();
System.out.println("不一定哪个先执行2:任务执行完成,结果为:" + result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
Java 线程的同步与交互机制
Java线程的同步与交互机制是确保多线程应用程序中的数据同步和协调的关键部分。
在Java中,可以使用以下机制来实现线程同步和交互:
Synchronized 关键字:
使用synchronized关键字可以确保只有一个线程可以访问关键代码段。它防止了多个线程同时访问共享资源,从而避免了数据的不一致性和竞争条件的问题。
wait()、notify()和notifyAll()方法:
这些方法允许线程之间进行通信和协调。当一个线程调用wait()方法时,它会释放它所持有的锁,并进入等待状态。当另一个线程调用notify()或notifyAll()方法时,等待线程将被唤醒并尝试重新获得锁来继续执行。
Lock 和 Condition 接口:
Java提供了Lock和Condition接口来提供更加灵活和强大的同步机制。与synchronized关键字不同,Lock接口允许更多的灵活性,例如可重入、超时等待和公平性。Condition接口允许线程在等待时释放锁,并在特定条件得到满足时重新获取锁。
volatile 关键字:
volatile关键字用于确保共享变量的可见性和原子性。当一个变量被声明为volatile时,每次读取该变量时,它都会从主内存中重新读取,而不是从缓存中读取。此外,volatile变量的写入操作也是原子性的。
这些机制可以用于在多线程应用程序中实现数据同步和协调,从而避免竞争条件和数据不一致性的问题。
还没有评论,来说两句吧...