目录
1.1中央处理器(CPU,Central Processing Unit)
4.2.6Volatile和Synchronization对比
5.8并发集合(Concurrent Collections)
一、:相关概念
1.1中央处理器(CPU,Central Processing Unit)
CPU 是计算机的中央运算单元,用来计算的。它从内存里面读取指令,然后执行
CPU 调度线程来执行任务。在多核 CPU 中,每个核心可以独立调度和执行线程。在单核 CPU 中,操作系统使用时间片轮转机制来实现多线程并发执行
1.2程序、进程、线程
程序:是指含有指令和数据的文件,被存储在磁盘或其他的数据设备中,也就是说程序是静态的代码;
进程:是程序的一次执行过程,是代码在数据集合上的一次运行活动,是系统资源分配和调度的基本单位;
线程:线程是进程中的一个实体,是被系统独立调度和分派的基本单位,一个进程中至少有一个线程,进程中的多个线程共享进程的资源;
简而言之,一个程序至少有一个进程,一个进程至少有一个线程
补充:
进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭
1.3线程与任务
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程内可以包含多个线程,线程是资源调度的最小单位。线程本质上就是执行任务的对象,如果把线程比作一台打印机,那么任务就是一份需要打印的文档
二、线程的创建:
在 Java 中,有两种方法可以创建线程并提供将在该线程中运行的代码:
提供一个实现了 Runnable 接口的对象。Runnable 接口定义了一个 run 方法,其中包含了线程要执行的代码。
通过继承 Thread 类并重写 run 方法来创建一个子类。
此外,Java 还提供了 Executor 框架,它允许您将任务提交给一个线程池来执行,而不是直接创建新的线程。
2.1继承Thread创建线程:
- 扩展 Thread类,
- 重写父类的run()方法,
- 规定线程的具体操作 调用Thread类的start()方法启动线程
class MyThread extends Thread {
@Override
public void run() {
// 在这里编写线程要执行的代码
System.out.println("MyThread running");
}
}
public class Main {
public static void main(String[] args) {
// 创建一个 MyThread 对象
MyThread myThread = new MyThread();
// 启动线程
myThread.start();
}
}
我们定义了一个名为
MyRunnable
的类,它实现了Runnable
接口。在MyRunnable
类中,我们重写了run
方法,该方法包含了线程要执行的代码。在
main
方法中,我们创建了一个MyRunnable
对象,并使用该对象创建了一个线程。然后我们调用start
方法来启动线程。当线程启动后,它将执行MyRunnable
类中的run
方法。
使用Thread子类创建线程的优缺点
2.2实现Runnable接口创建线程:
- 使用Thread创建线程对象时,使用的构造方法:
- Thread(Runnable target)
- Thread(Runnable target,String name)
- 该构造方法中的参数是一个Runnable类型的接口,因此,在创建线程对象时必须向构造方法的参数传递一个实现Runnable接口类的实例,该实例对象称作所创线程的目标对象,当线程调用start()方法后,一旦轮到它来享用CPU资源,目标对象就会自动调用接口中的run()方法(接口回调)
- Runnable接口只有一个抽象方法run()
class MyRunnable implements Runnable {
@Override
public void run() {
// 在这里编写线程要执行的代码
System.out.println("MyRunnable running");
}
}
public class Main {
public static void main(String[] args) {
// 创建一个实现了 Runnable 接口的对象
MyRunnable myRunnable = new MyRunnable();
// 使用该对象创建一个线程
Thread thread = new Thread(myRunnable);
// 启动线程
thread.start();
}
}
我们定义了一个名为
MyThread
的类,它继承了Thread
类。在MyThread
类中,我们重写了run
方法,该方法包含了线程要执行的代码。在
main
方法中,我们创建了一个MyThread
对象,并调用start
方法来启动线程。当线程启动后,它将执行MyThread
类中的run
方法。
2.3使用Callable和Future创建线程(了解)
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 在这里编写线程要执行的代码
int result = 0;
for (int i = 1; i <= 100; i++) {
result += i;
}
return result;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建一个线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
// 创建一个 Callable 对象
MyCallable myCallable = new MyCallable();
// 提交 Callable 对象并获取 Future 对象
Future<Integer> future = executor.submit(myCallable);
// 获取任务的结果
int result = future.get();
System.out.println("Result: " + result);
// 关闭线程池
executor.shutdown();
}
}
首先,我们定义了一个
MyCallable
类,它实现了Callable
接口。在call()
方法中,我们编写了线程要执行的代码。在这个例子中,线程计算了 1 到 100 的和。然后,在
main
方法中,我们创建了一个单线程的线程池,并创建了一个MyCallable
对象。接着,我们使用executor.submit(myCallable)
方法提交了这个Callable
对象,并获得了一个Future
对象。最后,我们调用future.get()
方法来获取任务的结果。注意,在调用
future.get()
方法时,主线程会阻塞,直到任务完成并返回结果为止。最后,我们调用executor.shutdown()
方法来关闭线程池。
2.4通过实现接口和继承类两种方式创建对象的比较:
采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。还便于目标对象的共享
使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。不能再继承其他类
2.5在并发编程中存在线程安全问题
主要原因有:
- 存在共享数据
- 多线程共同操作共享数据。
三、线程的生命周期:
3.1常见方法
Thread
类提供了许多用于管理线程的方法。下面是一些常用的方法:
start()
: 启动一个新线程并执行run()
方法中的代码。run()
: 定义线程要执行的代码。sleep(long millis)
: 使当前线程暂停执行指定的时间。join()
: 等待该线程终止。interrupt()
: 中断该线程。isInterrupted()
: 判断该线程是否被中断。currentThread()
: 返回当前正在执行的线程对象。
3.2sleep方法:
- Thread类提供,基于毫秒与基于纳秒的暂停时间
- sleep时间并不能保证准确,它们受底层操作系统设施的限制(大于等于sleep时间,继续执行,无法精确)
- sleep周期可以通过中断(Interrupt)来终止
// 定义一个类,继承 Thread 类
class MyThread extends Thread {
// 重写 run 方法,定义线程要执行的代码
@Override
public void run() {
// 循环打印 1 到 10 的数字
for (int i = 1; i <= 10; i++) {
System.out.println(i);
// 每次打印后暂停 1 秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
}
}
}
}
public class Main {
public static void main(String[] args) {
// 创建一个 MyThread 对象
MyThread myThread = new MyThread();
// 调用 start 方法来启动这个新线程
myThread.start();
}
}
补充TimeUnit Sleep
java.util.concurrent.TimeUnit
是一个枚举类,它提供了一组用于表示时间单位的常量。这些常量包括:
NANOSECONDS
: 纳秒,表示 10 的 -9 次方秒。MICROSECONDS
: 微秒,表示 10 的 -6 次方秒。MILLISECONDS
: 毫秒,表示 10 的 -3 次方秒。SECONDS
: 秒。MINUTES
: 分钟,表示 60 秒。HOURS
: 小时,表示 60 分钟。DAYS
: 天,表示 24 小时。
3.3interrupt()方法
中断(Interrupts),表示线程应停止正在执行的操作,并执行其他操作
由程序员决定,线程应如何响应中断
一个线程通过调用指定线程对象的interrupt()方法,发送一个中断通知,以使指定线程中断
被中断线程获取中断通知后,将抛出InterruptedException异常,因此,捕获异常决定中断后的操作
// 定义一个类,继承 Thread 类
class MyThread extends Thread {
// 重写 run 方法,定义线程要执行的代码
@Override
public void run() {
// 循环打印 1 到 10 的数字
for (int i = 1; i <= 10; i++) {
System.out.println(i);
// 每次打印后暂停 1 秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
// 如果线程被中断,退出循环
break;
}
}
}
}
public class Main {
public static void main(String[] args) {
// 创建一个 MyThread 对象
MyThread myThread = new MyThread();
// 调用 start 方法来启动这个新线程
myThread.start();
// 中断这个线程
myThread.interrupt();
}
}
3.4Joins 方法
- join()方法,允许一个线程等待另一个线程的完成
- 导致当前线程暂停执行,直到指定线程终止
- 与sleep相同,通过InterruptedException异常响应中断
// 定义一个类,继承 Thread 类
class MyThread extends Thread {
// 重写 run 方法,定义线程要执行的代码
@Override
public void run() {
// 循环打印 1 到 10 的数字
for (int i = 1; i <= 10; i++) {
System.out.println(i);
// 每次打印后暂停 1 秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
}
}
}
}
public class Main {
public static void main(String[] args) {
// 创建一个 MyThread 对象
MyThread myThread = new MyThread();
// 调用 start 方法来启动这个新线程
myThread.start();
// 等待这个线程结束
try {
myThread.join();
} catch (InterruptedException e) {
System.out.println("Main thread was interrupted");
}
System.out.println("Main thread finished");
}
}
在上面的代码中,我们在
main
方法中调用了myThread.join()
方法来等待这个线程结束。当这个线程结束后,join()
方法会返回,主线程会继续执行。注意,
join()
方法会抛出InterruptedException
异常,因此我们需要使用try-catch
语句来捕获并处理这个异常。
3.5yield()方法
yield():让出一次执行机会,使线程从运行状态转到可运行状态
yield()
方法是Thread
类的一个静态方法,它用于暂停当前正在执行的线程,让出 CPU 时间片给其他线程。不过,需要注意的是,这个方法只是一个提示,它并不能保证当前线程一定会暂停执行。
// 定义一个类,继承 Thread 类
class MyThread extends Thread {
// 线程的名称
private String name;
// 构造方法,用于设置线程的名称
public MyThread(String name) {
this.name = name;
}
// 重写 run 方法,定义线程要执行的代码
@Override
public void run() {
// 循环打印 1 到 10 的数字
for (int i = 1; i <= 10; i++) {
System.out.println(name + ": " + i);
// 如果 i 是 5 的倍数,调用 yield 方法
if (i % 5 == 0) {
Thread.yield();
}
}
}
}
public class Main {
public static void main(String[] args) {
// 创建两个 MyThread 对象
MyThread thread1 = new MyThread("Thread 1");
MyThread thread2 = new MyThread("Thread 2");
// 调用 start 方法来启动这两个新线程
thread1.start();
thread2.start();
}
}
我们定义了一个
MyThread
类,它继承了Thread
类。在run()
方法中,我们编写了线程要执行的代码。在这个例子中,线程会打印 1 到 10 的数字,并在每次打印到 5 的倍数时调用yield()
方法。然后,在
main
方法中,我们创建了两个MyThread
对象,并调用了它们的start()
方法来启动这两个新线程。当这两个线程运行时,它们会交替打印数字,并在打印到 5 的倍数时调用yield()
方法。
3.5wait()和notify()方法
wait()方法的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,当前线程被唤醒(进入“就绪状态”)
notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程
class MyThread extends Thread {
private final Object lock = new Object();
@Override
public void run() {
synchronized (lock) {
for (int i = 1; i <= 10; i++) {
System.out.println(i);
try {
Thread.sleep(1000);
if (i == 5) {
// 当 i 等于 5 时,调用 lock.wait() 方法来使当前线程等待
lock.wait();
}
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
}
}
}
}
public void wakeUp() {
synchronized (lock) {
// 调用 lock.notify() 方法来唤醒等待的线程
lock.notify();
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(9000);
// 调用 myThread.wakeUp() 方法来唤醒等待的线程
myThread.wakeUp();
}
}
四、线程的同步
4.1volatile关键字
volatile 是一个 Java 关键字,它用于修饰变量。当一个变量被声明为 volatile 时,它表示这个变量是易变的,可能会被多个线程同时访问和修改。
volatile 关键字可以保证变量的可见性和有序性。可见性指的是当一个线程修改了一个 volatile 变量时,其他线程能够立即看到这个修改。有序性指的是禁止编译器对 volatile 变量进行指令重排。
// 定义一个类,继承 Thread 类
class MyThread extends Thread {
// 使用 volatile 关键字修饰变量
private volatile boolean running = true;
// 重写 run 方法,定义线程要执行的代码
@Override
public void run() {
// 循环打印 1 到 10 的数字
for (int i = 1; i <= 10 && running; i++) {
System.out.println(i);
// 每次打印后暂停 1 秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
}
}
}
public void stopRunning() {
running = false;
}
}
public class Main {
public static void main(String[] args) {
// 创建一个 MyThread 对象
MyThread myThread = new MyThread();
// 调用 start 方法来启动这个新线程
myThread.start();
// 停止这个线程
myThread.stopRunning();
}
}
4.1.5背景
线程主要通过访问共享数据实现通信 这种通信形式非常有效,但会产生两种错误:线程冲突与内存一致性的错误。防止这些错误所需的工具是同步
4.2synchronized关键字
synchronized
是一个 Java 关键字,它用于修饰方法或代码块。当一个方法或代码块被声明为synchronized
时,它表示这个方法或代码块是同步的,只能被一个线程同时访问。
synchronized
关键字可以保证线程安全,防止多个线程同时访问和修改共享数据。当一个线程进入synchronized
方法或代码块时,它会获得一个锁;当这个线程离开synchronized
方法或代码块时,它会释放这个锁。只有获得锁的线程才能进入synchronized
方法或代码块,其他线程必须等待锁被释放。
- 同步普通方法(实例方法)上锁,锁是当前实例对象 ,进入方法前要获得当前实例的锁,方法执行完释放。
- 同步静态方法,锁是当前类的class对象 ,进入方法前前要获得当前类对象的锁,方法执行完释放。
- 同步代码块,要指定锁的对象,是可以是实例对象,也可以是类对象,进入同步代码块前要获得给定对象的锁,代码块执行完释放
4.2.1实例方法
public class SynchronizedExample {
private int count = 0; // 定义一个私有变量count
public synchronized void incrementCount() { // 使用synchronized关键字修饰实例方法
count++; // 每次调用方法时,count自增1
}
public void runExample() {
Thread thread1 = new Thread(new Runnable() { // 创建线程1
@Override
public void run() {
for (int i = 0; i < 10000; i++) { // 循环10000次
incrementCount(); // 调用incrementCount方法
}
}
});
Thread thread2 = new Thread(new Runnable() { // 创建线程2
@Override
public void run() {
for (int i = 0; i < 10000; i++) { // 循环10000次
incrementCount(); // 调用incrementCount方法
}
}
});
thread1.start(); // 启动线程1
thread2.start(); // 启动线程2
try {
thread1.join(); // 等待线程1执行完毕
thread2.join(); // 等待线程2执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count: " + count); // 输出count的值
}
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
example.runExample();
}
}
这个程序创建了两个线程,每个线程都会调用
incrementCount
方法10000次。由于该方法使用synchronized
关键字修饰,因此每次只能有一个线程访问该方法。这样可以确保count
变量的值在程序结束时为20000。
4.2.2同步静态方法
public class SynchronizedStaticExample {
private static int count = 0; // 定义一个私有静态变量count
public static synchronized void incrementCount() { // 使用synchronized关键字修饰静态方法
count++; // 每次调用方法时,count自增1
}
public void runExample() {
Thread thread1 = new Thread(new Runnable() { // 创建线程1
@Override
public void run() {
for (int i = 0; i < 10000; i++) { // 循环10000次
incrementCount(); // 调用incrementCount方法
}
}
});
Thread thread2 = new Thread(new Runnable() { // 创建线程2
@Override
public void run() {
for (int i = 0; i < 10000; i++) { // 循环10000次
incrementCount(); // 调用incrementCount方法
}
}
});
thread1.start(); // 启动线程1
thread2.start(); // 启动线程2
try {
thread1.join(); // 等待线程1执行完毕
thread2.join(); // 等待线程2执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count: " + count); // 输出count的值
}
public static void main(String[] args) {
SynchronizedStaticExample example = new SynchronizedStaticExample();
example.runExample();
}
}
在
incrementCount
方法中,我们使用了一个synchronized
代码块来同步对类对象SynchronizedStaticExample2.class
的访问。这样可以确保每次只能有一个线程访问该代码块,从而避免了多线程环境下对共享变量count
的竞争条件
4.2.3同步代码块
4.2.4注意
构造函数不能同步,在构造函数中使用synchronized关键字是语法错误。同步构造函数没有意义,因为只有创建对象的线程在构建时才能访问它,多线程调用将创建多个对象
4.2.5变量的可见性与并发执行操作的原子性
变量的可见性指的是当一个线程修改了一个共享变量的值后,其他线程能否立即看到这个修改。如果不能立即看到,那么这个变量就不具有可见性。Java 提供了一些机制来保证变量的可见性,例如使用
volatile
关键字修饰变量、使用同步块或方法等。并发执行操作的原子性指的是在多线程环境下,对共享变量的读写操作能够以原子方式完成,即不会被其他线程中断。如果一个操作不具有原子性,那么它可能会被其他线程中断,导致数据不一致等问题。Java 提供了一些机制来保证操作的原子性,例如使用同步块或方法、使用原子变量类等。
4.2.6Volatile和Synchronization对比
4.3CountDownLatch
CountDownLatch
是 Java 中的一个同步工具类,它允许一个或多个线程等待,直到一组操作完成。CountDownLatch
维护一个计数器,当计数器的值为 0 时,所有等待的线程都将被释放。
import java.util.concurrent.CountDownLatch;
class MyThread extends Thread {
private CountDownLatch latch;
public MyThread(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
// 循环打印 1 到 10 的数字
for (int i = 1; i <= 10; i++) {
System.out.println(i);
// 每次打印后暂停 1 秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
}
}
// 递减计数器的值
latch.countDown();
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
// 创建一个 CountDownLatch 对象并指定计数器的初始值为 1
CountDownLatch latch = new CountDownLatch(1);
// 创建一个 MyThread 对象并启动这个新线程
MyThread myThread = new MyThread(latch);
myThread.start();
// 等待计数器的值变为 0
System.out.println("Waiting for thread to finish");
latch.await();
System.out.println("Thread is done");
}
}
在这个示例中,我们创建了一个
CountDownLatch
对象并指定了计数器的初始值为 1。然后我们启动了一个新线程,并在该线程中调用latch.countDown()
方法来递减计数器的值。在主线程中,我们调用latch.await()
方法来等待计数器的值变为 0。当新线程运行完毕并调用
countDown()
方法后,计数器的值将变为 0,主线程将从await()
方法返回并继续执行。
五、Lock接口
5.1内部锁(Intrinsic Locks)
内部锁(Intrinsic Locks),也称为监视器锁(Monitor Locks),是 Java 中每个对象都具有的一种锁。当一个线程进入一个同步块或方法时,它会自动获取该对象的内部锁。当线程离开同步块或方法时,它会自动释放该对象的内部锁。
内部锁用于实现线程同步,确保在同一时间只有一个线程能访问共享资源
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
在这个示例中,
increment
和getCount
方法都被声明为synchronized
。这意味着当一个线程调用这些方法时,它会自动获取Counter
对象的内部锁。由于同一时间只有一个线程能持有内部锁,因此这些方法在同一时间只能被一个线程调用。
5.2原子访问(Atomic Access)
原子访问(Atomic Access)指的是在多线程环境下,对变量的读写操作能够以原子方式完成,即不会被其他线程中断。Java 提供了一些原子变量类,如
AtomicInteger
、AtomicLong
和AtomicReference
等,它们能够保证对变量的读写操作是原子的。原子性操作:要么完全执行,要么不执行 对于引用变量和大多数基本变量(long和double超过32bit在某些虚拟机按2次读写),读取和写入均为原子性操作
5.3锁对象(Lock Objects)
锁对象(Lock Objects)是 Java 提供的一种用于实现线程同步的工具。它提供了比内部锁(Intrinsic Locks)更灵活的锁定机制,允许线程以更细粒度的方式来控制对共享资源的访问。
Lock,是用于控制多线程访问共享资源的工具,提供对共享资源的独占访问权限:一次只有一个线程可以获取该锁,并且对共享资源的访问首先获取该锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
// 创建一个 ReentrantLock 对象
private Lock lock = new ReentrantLock();
public void increment() {
// 获取锁
lock.lock();
try {
// 递增 count 的值
count++;
} finally {
// 释放锁
lock.unlock();
}
}
public int getCount() {
// 获取锁
lock.lock();
try {
// 返回 count 的值
return count;
} finally {
// 释放锁
lock.unlock();
}
}
}
public class Main {
public static void main(String[] args) {
// 创建一个 Counter 对象
Counter counter = new Counter();
// 调用 increment 方法来递增 count 的值
counter.increment();
// 调用 getCount 方法来获取 count 的当前值并将其打印出来
System.out.println(counter.getCount());
}
}
在这个示例中,我们定义了一个名为 Counter 的类,该类使用 ReentrantLock 对象来保护对其 count 变量的访问。Counter 类有两个方法:increment() 和 getCount()。increment() 方法用于递增 count 的值,而 getCount() 方法用于获取 count 的当前值。在 Main 类的 main 方法中,创建了一个 Counter 对象并调用了它的 increment() 和 getCount() 方法。
5.4Executor(执行器)
提供用于管理/产生用于跟踪一个或多个异步任务进度的方法
java.util.concurrent包,定义了三个Executor接口:
- Executor,支持启动新任务的简单接口 ExecutorService,
- Executor子接口,增加了线程的生命周期管理特性
- ScheduledExecutorService,ExecutorService子接口,支持Future/定期执行任务
5.5线程池(Thread Pools)
线程池(Thread Pools)是一种常用的并发模式,它可以帮助我们更好地管理线程资源。在线程池中,一组固定大小的线程被创建并等待执行任务。当有新任务到来时,线程池中的一个线程会被分配执行该任务。任务执行完成后,该线程会返回线程池等待下一个任务。
5.6原子变量(Atomic Variables)
原子变量(Atomic Variables)是一种特殊类型的变量,它可以在多线程环境下安全地进行读写操作。Java 中的 java.util.concurrent.atomic 包提供了一些原子变量类,包括 AtomicInteger、AtomicLong、AtomicBoolean 和 AtomicReference。这些类分别表示可以原子更新的 int、long、boolean 和对象引用。
所有的原子变量类都有 get 和 set 方法,它们的工作方式类似于对 volatile 变量的读写操作。也就是说,set 操作与同一变量上任何后续 get 操作之间具有 happens-before 关系。原子 compareAndSet 方法也具有这些内存一致性特性,简单的原子算术方法也适用于整数原子变量
5.7线程局部变量(ThreadLocal)
ThreadLocal是
java.lang
包中的一个构造,它允许您存储仅能被特定线程访问的数据。它提供了线程局部变量,这些变量与它们的普通副本不同,每个访问一个线程局部变量(通过其get
或set
方法)的线程都有其自己独立初始化的变量副本。java.lang.ThreadLocal<T>类,提供线程局部变量
5.8并发集合(Concurrent Collections)
Concurrent Collections是一组线程安全的集合类,它们可以在多线程环境中安全地使用。在Java中,
java.util.concurrent
包中包含了许多对Java集合框架的补充,这些补充最容易通过提供的集合接口进行分类。例如,BlockingQueue定义了一个先进先出的数据结构,当您尝试向满队列添加或从空队列检索时会阻塞或超时;ConcurrentMap是java.util.Map的子接口,它定义了有用的原子操作。