选错了就选错了,不要一遍一遍的后悔,总是一遍遍的想,当时怎么样就好了,不要欺负当时的自己,当时你一个人站在迷雾中,也很迷茫,就算重新来一遍,以你当时的阅历和心智,还是会选择同样的选项,不要去美化那条你没选择的路,要相信,你脚下的路就是最好的,终会有星辰大海,花开锦簇,不用回头看,也不必批判当时的自己,总会有不同的人陪你看同样的风景,勇敢点,向前走。
目录
介绍
除了CompletableFuture
所提供的异步编程模型之外,Java还为并发编程提供了丰富的工具和类库,这些工具能够帮助开发者更高效、安全地编写多线程应用程序。以下是几个重要的类别及其代表性组件:
原子变量(Atomic Variables)
原子变量是Java并发编程中非常重要的组成部分,它们允许在不使用显式锁的情况下执行线程安全的操作。Java提供了多种类型的原子变量,例如AtomicInteger
、AtomicLong
等基本类型,以及AtomicReference
用于引用类型的原子更新. 这些类内部使用了比较并交换(Compare-And-Swap, CAS)机制来确保操作的原子性,从而避免了传统锁带来的性能开销。例如,AtomicInteger
支持诸如incrementAndGet()
这样的方法,可以在多线程环境中安全地递增一个整数值,并返回最新的结果。
import java.util.concurrent.atomic.AtomicInteger; public class AtomicExample { private static AtomicInteger counter = new AtomicInteger(0); public static void main(String[] args) { // 安全地增加计数器 System.out.println("Counter: " + counter.incrementAndGet()); } }
并发集合(Concurrent Collections)
为了满足多线程环境下的高效访问需求,Java引入了一系列线程安全且高效的集合实现。比如ConcurrentHashMap
,它不仅提供了比Hashtable
更好的并发性能,而且还保持了一定程度上的读写隔离,使得读取操作几乎不会受到写入操作的影响. 另外还有像CopyOnWriteArrayList
这样的列表实现,当迭代器遍历列表时,即使其他线程修改了列表内容也不会抛出ConcurrentModificationException
异常,因为每次修改都会创建一个新的副本.
import java.util.concurrent.ConcurrentHashMap; public class ConcurrentMapExample { private static ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(); public static void main(String[] args) { // 线程安全地向map中添加键值对 map.put("key", "value"); System.out.println("Value: " + map.get("key")); } }
同步辅助类(Synchronization Aids)
同步辅助类主要用于协调多个线程间的协作行为,常见的有CountDownLatch
、CyclicBarrier
、Semaphore
等。CountDownLatch
可以让一个或多个线程等待其他线程完成特定数量的任务后继续执行;而CyclicBarrier
则适用于一组线程需要相互等待到达某个屏障点的情况,一旦所有参与方都到达了这个点,它们就可以一起继续前进。此外,Semaphore
可以用来控制同时访问某资源的最大线程数目,这对于限制数据库连接池大小等情况特别有用.
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CountDownLatchExample { private static final int N_THREADS = 3; private static CountDownLatch latch = new CountDownLatch(N_THREADS); public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(N_THREADS); for (int i = 0; i < N_THREADS; ++i) { executor.submit(() -> { try { Thread.sleep((long) (Math.random() * 1000)); System.out.println(Thread.currentThread().getName() + " is done."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { latch.countDown(); // 减少计数 } }); } latch.await(); // 主线程等待所有任务完成 System.out.println("All threads have finished their work."); executor.shutdown(); } }
综上所述,Java提供的并发工具涵盖了从低级别的原子操作到高级别的任务协调等多个层面,这些工具极大地简化了并发程序的设计与实现过程,同时也提高了程序的可靠性和效率. 开发者可以根据具体的应用场景选择合适的工具,以构建更加健壮、高效的并发应用
原子变量(Atomic Variables)
原子变量是Java并发编程中非常重要的工具,它们允许在不使用显式锁的情况下执行线程安全的操作。Java提供了多种类型的原子变量,例如AtomicInteger
、AtomicLong
等基本类型,以及AtomicReference
用于引用类型的原子更新. 这些类内部使用了比较并交换(Compare-And-Swap, CAS)机制来确保操作的原子性,从而避免了传统锁带来的性能开销。
AtomicInteger 示例代码
import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerExample { private static final AtomicInteger counter = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { // 创建多个线程同时对counter进行递增操作 Thread[] threads = new Thread[10]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread(() -> { for (int j = 0; j < 1000; j++) { counter.incrementAndGet(); // 安全地递增计数器 } }); threads[i].start(); } // 等待所有线程完成 for (Thread thread : threads) { thread.join(); } System.out.println("Final count: " + counter.get()); // 输出最终计数值 } }
这段代码展示了如何利用AtomicInteger
的安全递增方法incrementAndGet()
来保证即使在高并发情况下也能正确地更新共享变量counter
,而无需使用synchronized
关键字或显式的锁对象。
并发集合(Concurrent Collections)
为了满足多线程环境下的高效访问需求,Java引入了一系列线程安全且高效的集合实现。比如ConcurrentHashMap
,它不仅提供了比Hashtable
更好的并发性能,而且还保持了一定程度上的读写隔离,使得读取操作几乎不会受到写入操作的影响. 另外还有像CopyOnWriteArrayList
这样的列表实现,当迭代器遍历列表时,即使其他线程修改了列表内容也不会抛出ConcurrentModificationException
异常,因为每次修改都会创建一个新的副本.
ConcurrentHashMap 示例代码
import java.util.concurrent.ConcurrentHashMap; import java.util.Map; public class ConcurrentHashMapExample { private static final Map<String, Integer> map = new ConcurrentHashMap<>(); public static void main(String[] args) { // 插入键值对 map.put("apple", 1); map.put("banana", 2); map.put("orange", 3); // 线程安全地计算每个水果的数量 map.computeIfPresent("apple", (k, v) -> v + 1); map.computeIfAbsent("grape", k -> 1); // 打印结果 System.out.println(map); // 输出 {apple=2, banana=2, orange=3, grape=1} } }
此示例说明了如何使用ConcurrentHashMap
的方法如computeIfPresent()
和computeIfAbsent()
来进行线程安全的数据处理。这些方法可以在不存在给定键的情况下添加新条目,或者在存在时更新现有条目的值,而且整个过程都是原子性的。
同步辅助类(Synchronization Aids)
同步辅助类主要用于协调多个线程间的协作行为,常见的有CountDownLatch
、CyclicBarrier
、Semaphore
等。CountDownLatch
可以让一个或多个线程等待其他线程完成特定数量的任务后继续执行;而CyclicBarrier
则适用于一组线程需要相互等待到达某个屏障点的情况,一旦所有参与方都到达了这个点,它们就可以一起继续前进。此外,Semaphore
可以用来控制同时访问某资源的最大线程数目,这对于限制数据库连接池大小等情况特别有用.
CountDownLatch 示例代码
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CountDownLatchExample { private static final int N_THREADS = 3; private static final CountDownLatch latch = new CountDownLatch(N_THREADS); public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(N_THREADS); // 提交任务给线程池 for (int i = 0; i < N_THREADS; ++i) { executor.submit(() -> { try { System.out.println(Thread.currentThread().getName() + " is working..."); Thread.sleep((long)(Math.random() * 1000)); // 模拟工作时间 System.out.println(Thread.currentThread().getName() + " has finished."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { latch.countDown(); // 减少计数器 } }); } // 主线程等待所有任务完成 latch.await(); System.out.println("All tasks have been completed."); executor.shutdown(); } }
在这个例子中,CountDownLatch
被用来让主线程等待所有子线程完成它们的工作。每个子线程完成自己的任务后调用countDown()
方法减少计数器的值,直到计数器归零,此时主线程将继续执行。
CyclicBarrier 示例代码
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class CyclicBarrierExample { private static final int N_PARTIES = 3; private static final CyclicBarrier barrier = new CyclicBarrier(N_PARTIES, () -> { System.out.println("All parties have arrived at the barrier."); }); public static void main(String[] args) { // 模拟N个参与者 for (int i = 0; i < N_PARTIES; ++i) { new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " is preparing..."); Thread.sleep((long)(Math.random() * 1000)); // 模拟准备时间 System.out.println(Thread.currentThread().getName() + " is ready."); barrier.await(); // 等待其他参与者 } catch (InterruptedException | BrokenBarrierException e) { Thread.currentThread().interrupt(); } }).start(); } } }
这里展示了CyclicBarrier
的作用,它可以用来同步多个线程,在所有线程都达到了某个共同的“屏障”之后才允许继续执行。当所有线程都到达屏障时,会触发一个可选的动作(如上面的例子中的打印语句),然后所有线程继续执行。
Semaphore 示例代码
import java.util.concurrent.Semaphore; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SemaphoreExample { private static final int MAX_PERMITS = 5; private static final Semaphore semaphore = new Semaphore(MAX_PERMITS); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(10); // 提交多个任务 for (int i = 0; i < 20; ++i) { executor.submit(() -> { try { semaphore.acquire(); // 请求许可 System.out.println(Thread.currentThread().getName() + " acquired a permit."); Thread.sleep((long)(Math.random() * 1000)); // 模拟占用资源的时间 System.out.println(Thread.currentThread().getName() + " releasing a permit."); semaphore.release(); // 释放许可 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } executor.shutdown(); } }
最后,Semaphore
示例展示了如何使用信号量来限制同时访问某种资源的线程数量。在这个例子中,我们设定了最多只能有五个线程同时持有信号量,因此即使提交了二十个任务,也只有不超过五个任务能够同时执行,其余的任务将排队等待空闲的信号量。
综上所述,Java提供的并发工具涵盖了从低级别的原子操作到高级别的任务协调等多个层面,这些工具极大地简化了并发程序的设计与实现过程,同时也提高了程序的可靠性和效率. 开发者可以根据具体的应用场景选择合适的工具,以构建更加健壮、高效的并发应用。