Java 并发包(java.util.concurrent,简称 JUC)提供了一系列的工具和框架,用于简化并发编程。以下是 JUC 包中常见类的介绍:
-
Callable:
Callable
接口是 Java 提供的一个带返回值的任务接口,类似于Runnable
接口,但是它可以在执行结束后返回一个结果,也可以抛出异常。通常与ExecutorService
结合使用,可以提交给线程池执行。public class CallableUse { public static void main(String[] args) { //第一种写法 FutureTask<Integer> futureTask1 = new FutureTask<Integer>(new Callable<Integer>() { public Integer call() { int sum = 0; for (int i = 0; i < 1001; i++) { sum += i; } return sum; } }); Thread thread = new Thread(futureTask1); thread.start(); //第二种写法 Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i < 1001; i++) { sum += i; } return sum; } }; FutureTask<Integer> futureTask2 = new FutureTask<>(callable); Thread thread2 = new Thread(futureTask2); thread2.start(); } }
实现
Callable
接口的类必须实现call()
方法,该方法定义了需要执行的任务,并可以返回一个结果。通过ExecutorService
的submit(Callable)
方法提交一个Callable
任务,返回一个Futuretask
对象,可以通过该对象获取任务执行的结果
-
ReentrantLock:
ReentrantLock
是一种可重入锁,它提供了与synchronized
关键字相同的功能,即确保代码块在同一时刻只有一个线程执行,但相比synchronized
更灵活,例如它支持公平性和非公平性,以及可中断性等特性。import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { private static final Lock lock = new ReentrantLock(); public static void main(String[] args) { new Thread(() -> { lock.lock(); try { System.out.println("Thread 1 acquired the lock"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }).start(); new Thread(() -> { lock.lock(); try { System.out.println("Thread 2 acquired the lock"); } finally { lock.unlock(); } }).start(); } }
相对于synchronized,优势有三:
-
其提供了trylock(超过时间):加锁,如果得不到锁对象,一段时间后放弃加锁。
-
可以是公平锁,在构造方法new ReentrantLock();可以给一个true开启
-
更加强大的唤醒机制,synchronized基于锁的wait与notify,唤醒的线程随机,reentrantlock可以搭配Condition类实现唤醒,可以唤醒指定线程
-
- 使用
ReentrantLock
的典型操作包括:- 调用
lock()
方法获取锁。 - 在
try
-finally
块中执行需要保护的临界区代码。 - 调用
unlock()
方法释放锁。
- 调用
- 可以使用
ReentrantLock
的tryLock()
方法尝试获取锁而不阻塞,或者使用lockInterruptibly()
方法在获取锁的过程中可以响应中断
-
原子类:
- JUC 提供了一系列的原子类,如
AtomicInteger
、AtomicLong
、AtomicBoolean
等,它们提供了一种线程安全的方式来进行原子操作,保证了操作的原子性。这些类底层使用了 CAS(Compare And Swap)操作来保证线程安全,常用于需要高效并发操作的场景。详见文章常见锁策略,synchronized内部原理以及CAS-CSDN博客
- JUC 提供了一系列的原子类,如
-
线程池:
- 线程池是管理线程的一种机制,它可以重用已创建的线程,减少了线程创建和销毁的开销,并且可以控制并发线程的数量。JUC 提供了
ThreadPoolExecutor
类来实现线程池,也提供了一些方便的工厂方法,如Executors.newFixedThreadPool()
、Executors.newCachedThreadPool()
等来创建不同类型的线程池。详见文章Java线程池 ThreadPoolExecutor, Executor-CSDN博客
- 线程池是管理线程的一种机制,它可以重用已创建的线程,减少了线程创建和销毁的开销,并且可以控制并发线程的数量。JUC 提供了
-
Semaphore:
Semaphore
是一种计数信号量,它可以限制同时访问某个资源的线程数量。它维护了一定数量的许可证,每次线程访问资源前需要获取许可证,如果许可证数量不足,则线程将被阻塞。一旦线程使用完资源,它将释放许可证,使得其他线程可以继续访问资源。import java.util.concurrent.Semaphore; public class SemaphoreExample { private static final Semaphore semaphore = new Semaphore(2); public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println("Thread acquired semaphore"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); System.out.println("Thread released semaphore"); } }).start(); } } }
Semaphore
的典型操作包括:调用acquire()
方法获取一个许可证,如果没有可用的许可证则会阻塞。在try
-finally
块中执行需要获取许可证保护的代码。调用release()
方法释放一个许可证。(PV操作)
-
CountDownLatch:
CountDownLatch
是一种同步工具,它可以使一个或多个线程等待其他线程执行完特定操作后再继续执行。它通过一个计数器来实现,当计数器减为零时,等待的线程可以继续执行。CountDownLatch
常用于实现线程之间的协调和同步。public class CountDownLatchUse { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { Thread thread = new Thread(()->{ int delay = (int) (Math.random()*10000+1000); try { Thread.sleep(delay); } catch (InterruptedException e) { throw new RuntimeException(e); } countDownLatch.countDown(); System.out.println(Thread.currentThread().getId()); }); thread.start(); } countDownLatch.await(); System.out.println("the end"); } }
CountDownLatch
的典型操作包括:- 创建
CountDownLatch
对象时指定初始计数器值。 - 在需要等待的线程调用
await()
方法等待计数器归零。 - 在其他线程中执行操作完成后,调用
countDown()
方法递减计数器。
- 创建
- 可以通过
await(long timeout, TimeUnit unit)
方法设置等待超时时间,或者通过getCount()
方法获取当前计数器值。
综上一些方式,都是为了解决多线程下线程安全以及提高效率的方式。