一、基本概念
1.1 什么是进程?什么是线程?
进程是: 一个应用程序(1个进程是一个软件)。一个进程可以启动多个线程。
线程是:一个进程中的执行场景/执行单元。
1.2 进程和线程是什么关系?
进程:可以看做是现实生活当中的公司。
线程:可以看做是公司当中的某个员工。
进程A和进程B的
内存独立不共享
。eg:
魔兽游戏是一个进程
酷狗音乐是一个进程
这两个进程是独立的,不共享资源。
1.3 线程A和线程B是什么关系?
在java语言中:
线程A和线程B,堆内存
和 方法区
内存共享。但是 栈内存
独立,一个线程一个栈。
java中之所以有多线程机制,目的就是为了 提高程序的处理效率
。
并行:在同一时刻,有多个指令在多个CPU上同时执行
并发:在同一时刻,有多个指令在单个CPU上交替执行
1.4 问题思考
1. 使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束?
main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在压栈弹栈。
2.对于单核的CPU来说,真的可以做到真正的多线程并发吗?
对于多核的CPU电脑来说,真正的多线程并发是没问题的。4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。
单核的CPU表示只有一个大脑:
不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于CPU的处理速度极快,多个线程之间频繁切换执行,给别人的感觉是:多个事情同时在做
3. 什么是真正的多线程并发?
t1线程执行t1的。
t2线程执行t2的。t1不会影响t2,t2也不会影响t1
。这叫做真正的多线程并发。
4.为什么run()方法只能try…catch…不能throws?
因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常。
1.5 关于线程对象的生命周期
二、创建线程的三种方法
2.1 继承Thread类
编写一个类,直接 继承 java.lang.Thread
,重写 run方法
。
t.run() 不会启动线程,只是普通的调用方法而已。不会分配新的分支栈。(这种方式就是单线程。)
t.start() 方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
2.2 实现Runnable接口
编写一个类,实现 java.lang.Runnable
接口,实现run方法
。
这种方法比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。
2.3 实现Callable接口
和上面上中方法不同的是,这个可以获取线程运行的结果
三、线程常见的成员方法
四、线程调度
4.1.常见的线程调度模型有哪些?
抢占式调度模型:
那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
java采用的就是抢占式调度模型。
均分式调度模型:
平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
平均分配,一切平等。
有一些编程语言,线程调度模型采用的是这种方式。
4.2 java中提供了哪些方法是和线程调度有关系的呢
实例方法:
- 最低优先级1,默认优先级是5,最高优先级10
优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)
静态方法:
yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。
注意:在回到就绪之后,有可能还会再次抢到。
class MyThread1 extends Thread {
public void doSome(){
MyThread2 t = new MyThread2();
t.join(); // 当前线程进入阻塞,t线程执行,直到t线程结束。当前线程才可以继续。
}
}
class MyThread2 extends Thread{
}
五、多线程并发环境下,数据的安全问题
以后在开发中,我们的项目都是运行在服务器当中,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现完了。这些代码我们都不需要编写。
最重要的是: 你要知道,你编写的程序需要放到一个多线程的环境下运行,你更需要关注的是这些数据在多线程并发的环境下是否是安全的。(重点:★★★★★)
什么时候数据在多线程并发的环境下会存在安全问题呢?★★★★★
满足三个条件:
- 条件1:多线程并发。
- 条件2:有共享数据。
- 条件3:共享数据有修改的行为。
满足以上3个条件之后,就会存在线程安全问题
怎么解决线程安全问题呢?
线程排队执行。(不能并发)。用排队执行解决线程安全问题。
这种机制被称为:线程同步机制。
专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。
线程同步就是线程排队了,线程排队了就会
牺牲一部分效率
,没办法,数据安全第一位,只有数据安全了,我们才可以谈效率。数据不安全,没有效率的事儿。
两个专业术语:
异步编程模型:
线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,谁也不需要等谁,这种编程模型叫做:异步编程模型。
其实就是:多线程并发(效率较高),异步就是并发。
同步编程模型:
线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,两个线程之间发生了等待关系,这就是同步编程模型。
效率较低,线程排队执行,同步就是排队。
Java中有三大变量?★★★★★
实例变量:在堆中
静态变量:在方法区
局部变量:在栈中
以上三大变量中:局部变量永远都不会存在线程安全问题,因为局部变量不共享。(一个线程一个栈)局部变量在栈中,所以局部变量永远都不会共享。
实例变量在堆中,堆只有1个。静态变量在方法区中,方法区只有1个。
堆和方法区都是多线程共享的,所以可能存在线程安全问题。
总结:
局部变量+常量:不会有线程安全问题。
成员变量(实例+静态):可能会有线程安全问题。
六、线程安全
6.1 同步代码块解决线程安全问题
同步代码块:把操作共享的代码锁起来
特点1:锁默认打开,有一个线程进去,锁自动关闭
特点2:里面的代码全部执行完毕,线程出来,锁自动打开
Synchronized(锁){
操作共享数据的代码
}
同步方法:就是把synchronized关键字加到方法上
修饰符 synchronized 返回值类型 方法名(方法参数) {....}
特点1:同步方法使锁住方法里面所有的代码
特点2:锁对象不能自己指定。非静态:this;静态:当前类的字节码文件对象
6.2 Lock
死锁:两个线程互相等待,是一种错误,注意写代码时不要让两个锁互相嵌套
6.3 生产者和消费者
常见方法:
Desk类:
消费者:
生产者:
测试类:
6.4 阻塞队列实现等待唤醒机制
七、线程池
以前写多线程时的弊端:
弊端:
1.用到的时候就创建
2.用完之后线程消失
线程池主要核心原理:
1.创建一个池子,池子中间是空的
2.提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子。下回再次提交任务时,不需要再创建新的线程,直接复用已有的线程即可
3.但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
线程池创建临时线程只有在核心线程都在忙,队伍也已经排满的情况下创建临时线程
最后pool.submit()提交即可
自定义线程池小结
1.创建一个空的池子
2.有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程
不断的提交任务,会有以下三个临界点:
1.当线程池满时,再提交任务就会排队
2.当核心线程满,队伍满时,会创建临时线程
3.当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略
八、练习题
习题一:买电影票
/**
* 第一题Code
*/
package com.company.com.demo.one;
public class Mythread extends Thread{
//表示这个类所有的对象,都共享ticket数据
static int ticket = 0;
@Override
public void run(){
while (true){
//同步代码块
synchronized (Mythread.class){
if(ticket < 1000){
try {
// 每次领取时间为3000毫秒
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket++;
System.out.println(getName() + "正在卖第" + ticket + "张票" );
}else{
break;
}
}
}
}
}
package com.company.com.demo.one;
public class Sell {
public static void main(String[] args) {
//常见两个线程模拟售票
Mythread t1 = new Mythread();
Mythread t2 = new Mythread();
t1.setName("窗口1");
t2.setName("窗口2");
t1.start();
t2.start();
}
}
习题二:送礼品
/**
* 第二题Code
*/
package com.company.com.demo.two;
public class Mythread extends Thread{
//表示这个类所有的对象,都共享ticket数据
static int gifts = 100;
static int gift = 0;
@Override
public void run(){
while (true){
//同步代码块
synchronized (Mythread.class){
if(gifts > 10){
try {
// 每次领取时间为3000毫秒
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
gifts--;
gift++;
System.out.println(getName() + "正在赠送第" + gift + "个礼物,还剩余"+gifts+"份礼物!" );
}else{
System.out.println("礼物小于10份不在进行赠送");
break;
}
}
}
}
}
package com.company.com.demo.two;
public class Give {
public static void main(String[] args) {
//常见两个线程模拟售票
Mythread t1 = new Mythread();
Mythread t2 = new Mythread();
t1.setName("窗口1");
t2.setName("窗口2");
t1.start();
t2.start();
}
}
习题三:打印奇数字
/**
* 第3题Code
*/
package com.company.com.demo.three;
public class MyRunnable implements Runnable{
static int num = 1;
@Override
public void run(){
while(true){
synchronized (MyRunnable.class){
if (num == 1){
System.out.println(Thread.currentThread().getName() + "正在输出" + num);
num++;
}
else if(num >1 && num <100 && num % 2 == 0){
num++;
System.out.println(Thread.currentThread().getName() + "正在输出" + num);
}else if (num >= 100){
break;
} else{
num++;
}
}
}
}
}
package com.company.com.demo.three;
public class PrintNum {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
习题四:多线程抢红包
/**
* 第4题Code
*/
package com.company.com.demo.four;
import java.util.Random;
public class MyThread extends Thread{
//共享数据
//100 块分成三份
static double money = 100;
static int count = 3;
//最小的中奖金额
static final double MIN = 0.01;
/**
* 下面代码四步走:
* 1.循环
* 2.判断收否走到末尾(已经到末尾)
* 3.判断是否走到末尾(未到末尾)
* 4.
*/
@Override
public void run(){
//同步代码块
synchronized (MyThread.class){
if(count == 0){
//判断,共享数据是否到了末尾(已经到末尾)
System.out.println(getName()+"没有抢到红包!");
}else{
double prize = 0; //中奖金额
//判断,共享数据是否到了末尾
//最后一个红包,
if(count == 1){
prize = money;
}else{
Random r = new Random();//随机获取钱
double bounds = money - (count - 1) * MIN;
// prize = r.nextDouble(bounds); JDK17才能用
prize = r.nextDouble();
if (prize < MIN){
prize = money;
}
}
money = money - prize;
count--;
System.out.println(getName()+"抢到了"+prize+"元");
}
}
}
}
package com.company.com.demo.four;
public class RedPocket {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
MyThread t4 = new MyThread();
MyThread t5 = new MyThread();
t1.setName("xiao1");
t2.setName("xiao2");
t3.setName("xiao3");
t4.setName("xiao4");
t5.setName("xiao5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
习题五:抽奖箱抽奖
/**
* 第5题Code
*/
package com.company.com.demo.five;
import com.company.com.demo.four.MyThread;
import java.util.ArrayList;
import java.util.Collections;
public class Mythread extends Thread{
ArrayList<Integer> list;
public Mythread(ArrayList<Integer> list) {
this.list = list;
}
@Override
public void run(){
while(true){
synchronized (MyThread.class){
if (list.size() == 0){
break;
}else{
//继续抽奖
Collections.shuffle(list);
int prize = list.remove(0);
System.out.println(getName()+"又产生一个" + prize +"元大奖");
}}}}}
package com.company.com.demo.five;
import java.util.ArrayList;
import java.util.Collections;
public class Prize {
public static void main(String[] args) {
//创建奖池
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,10,5,50,20,100,200,500);
//创建线程
Mythread t1 = new Mythread(list);
Mythread t2 = new Mythread(list);
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
}
}
习题六:多线程统计并求最大值
/**
* 第6题Code
*/
package com.company.com.thread.six;
import com.company.com.thread.four.MyThread;
import java.util.ArrayList;
import java.util.Collections;
public class Mythread extends Thread{
ArrayList<Integer> list;
public Mythread(ArrayList<Integer> list) {
this.list = list;
}
//这里使用静态是因为,stati声明的只会创建一次,防止每次创建一个线程都会创建一个
static ArrayList<Integer> list1 = new ArrayList<>(); // 抽奖箱1的结果
static ArrayList<Integer> list2 = new ArrayList<>(); // 抽奖箱2的结果
static int sum1 = 0;
static int sum2 = 0;
@Override
public void run(){
while(true){
synchronized (MyThread.class){
if (list.size() == 0){
//抽奖结束
if ("抽奖箱1".equals(getName())){
System.out.println("抽奖箱1" + list1 + ",最大奖:"+ Collections.max(list1) + ",总计金额为:" + sum1);
}else {
System.out.println("抽奖箱2" + list2 + ",最大奖:"+ Collections.max(list2) + ",总计金额为:" + sum2);
}
break;
}else{
//继续抽奖
Collections.shuffle(list);
int prize = list.remove(0);
if ("抽奖箱1".equals(getName())){
list1.add(prize);
sum1 += prize;
}else {
list2.add(prize);
sum2 += prize;
}
// System.out.println(getName()+"又产生一个" + prize +"元大奖");
}
}
}
}
}
package com.company.com.thread.six;
import java.util.ArrayList;
import java.util.Collections;
public class Prize {
public static void main(String[] args) {
//创建奖池
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,10,5,50,20,100,200,500);
//创建线程
Mythread t1 = new Mythread(list);
Mythread t2 = new Mythread(list);
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
}
}
上述代码虽然解决了问题,但是还有些问题,创建 list1,list2各自写了一条语句,那如果我们有一百条线程呢?还有判断,用了if...else,难道也写一百个判断吗?显然是不合适的,对上述代码做出更改:
package com.company.com.thread.six;
import com.company.com.thread.four.MyThread;
import java.util.ArrayList;
import java.util.Collections;
public class Mythread extends Thread{
ArrayList<Integer> list;
public Mythread(ArrayList<Integer> list) {
this.list = list;
}
//这里使用静态是因为,stati声明的只会创建一次,防止每次创建一个线程都会创建一个
// static ArrayList<Integer> list1 = new ArrayList<>(); // 抽奖箱1的结果
// static ArrayList<Integer> list2 = new ArrayList<>(); // 抽奖箱2的结果
// static int sum1 = 0;
// static int sum2 = 0;
@Override
public void run(){
ArrayList<Integer> boxList = new ArrayList<>();
int sum = 0;
while(true){
synchronized (MyThread.class){
if (list.size() == 0){
//抽奖结束
System.out.println(getName() + boxList + ",最大奖:"+ Collections.max(boxList) + ",总计金额为:" + sum);
break;
}else{
//继续抽奖
Collections.shuffle(list);
int prize = list.remove(0);
boxList.add(prize);
sum += prize;
// System.out.println(getName()+"又产生一个" + prize +"元大奖");
}
}
}
}
}
习题七:多线程之间的比较
/**
* 第7题Code
*/
package com.company.com.thread.seven;
import com.company.com.thread.four.MyThread;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
ArrayList<Integer> list;
public MyCallable(ArrayList<Integer> list) {
this.list = list;
}
@Override
public Integer call() throws Exception {
ArrayList<Integer> boxList = new ArrayList<>();
int sum = 0;
while (true) {
synchronized (MyThread.class) {
if (list.size() == 0) {
//抽奖结束
System.out.println(Thread.currentThread().getName() + boxList + ",最大奖:" + Collections.max(boxList) + ",总计金额为:" + sum);
break;
} else {
//继续抽奖
Collections.shuffle(list);
int prize = list.remove(0);
boxList.add(prize);
sum += prize;
}
}
}
//返回boxList的最大值
if (boxList.size() == 0){
return null;
}else{
return Collections.max(boxList);
}
}
}
package com.company.com.thread.seven;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Prize {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建奖池
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,10,5,50,20,100,200,500);
//创建多线程要运行的参数对象
MyCallable mc = new MyCallable(list);
//创建多线程运行结果的管理者对象
FutureTask<Integer> ft1 = new FutureTask<>(mc);
FutureTask<Integer> ft2 = new FutureTask<>(mc);
//创建线程对象
Thread t1 = new Thread(ft1);
Thread t2 = new Thread(ft2);
//设置线程名字
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
//获取结果
Integer max1 = ft1.get();
Integer max2 = ft2.get();
System.out.println(max1);
System.out.println(max2);
}
}
九、子线程
题目:主线程进行for循环,每次循环到4的倍数的时候就开启子线程将该数写入txt文件
package com.company.com.thread.subThread.myTest;
import java.io.FileWriter;
import java.io.IOException;
public class myTest {
public static void main(String[] args) {
for (int i = 1; i <= 20; i++) { // 20次循环作为示例,你可以根据实际需求更改循环次数
System.out.println("Main Thread: Iteration " + i);
// 到达4的倍数时,启动子线程进行写文件
if (i % 4 == 0) {
int valueToWrite = i;
// 启动子线程
Thread writerThread = new Thread(() -> {
System.out.println("Sub Thread: Writing to file - " + valueToWrite);
writeToFile(valueToWrite);
});
writerThread.start();
}
try {
// 主线程休眠,模拟其他工作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static void writeToFile(int value) {
try (FileWriter writer = new FileWriter("output.txt", true)) {
// 追加写入文件
writer.write(value + System.lineSeparator());
} catch (IOException e) {
e.printStackTrace();
}
}
}
可以使用
Thread.currentThread().getName()
来获取当前执行线程的名称。在你的代码中,你可以给主线程和子线程命名,然后在输出语句中显示线程名称,以区分它们。
package com.company.com.thread.subThread.myTest;
import java.io.FileWriter;
import java.io.IOException;
public class mytest2 {
public static void main(String[] args) {
for (int i = 1; i <= 20; i++) {
System.out.println("Main Thread: Iteration " + i);
if (i % 4 == 0) {
int valueToWrite = i;
// 启动子线程并命名
Thread writerThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": Writing to file - " + valueToWrite);
writeToFile(valueToWrite);
}, "WriterThread-" + i);
// 启动子线程
writerThread.start();
}
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static void writeToFile(int value) {
try (FileWriter writer = new FileWriter("output.txt", true)) {
writer.write(value + System.lineSeparator());
} catch (IOException e) {
e.printStackTrace();
}
}
}
子线程使用线程池实现
package com.company.com.thread.subThread.myTest;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.*;
public class myTest3 {
public static void main(String[] args) {
//Method 1
// ExecutorService executorService = Executors.newFixedThreadPool(5); // 创建包含5个线程的线程池
//
//Method 2
int poolSize = 5; // 线程池大小
int maxPoolSize = 10; // 最大线程池大小
long keepAliveTime = 5000; // 保持线程活动时间(毫秒)
ExecutorService executorService = new ThreadPoolExecutor(
poolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
for (int i = 1; i <= 20; i++) {
System.out.println("Main Thread: Iteration " + i);
if (i % 4 == 0) {
int valueToWrite = i;
// 提交任务到线程池
executorService.submit(() -> {
System.out.println(Thread.currentThread().getName() + ": Writing to file - " + valueToWrite);
writeToFile(valueToWrite);
});
}
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 关闭线程池
executorService.shutdown();
}
private static void writeToFile(int value) {
try (FileWriter writer = new FileWriter("output.txt", true)) {
// 写入文件前等待3秒,模拟等待操作
Thread.sleep(1000);
writer.write(value + System.lineSeparator());
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
通过ThreadPoolExecutor
构造函数明确设置了线程池的核心大小、最大大小和保持活动时间。这些参数可以根据实际需求进行调整。请注意,这里使用了LinkedBlockingQueue
作为任务队列,你也可以根据需要选择其他类型的队列。
CountDownLatch
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) {
// 创建一个 CountDownLatch,初始值为2
CountDownLatch latch = new CountDownLatch(2);
// 创建并启动两个子线程
new Thread(() -> {
System.out.println("Thread 1 is doing some work...");
// 模拟子线程执行任务
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1 is done.");
// 计数器减一
latch.countDown();
}).start();
new Thread(() -> {
System.out.println("Thread 2 is doing some work...");
// 模拟子线程执行任务
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2 is done.");
// 计数器减一
latch.countDown();
}).start();
try {
// 主线程等待计数器变为零
System.out.println("Main Thread is waiting...");
latch.await();
System.out.println("Main Thread continues.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这个例子中,主线程等待两个子线程执行完毕后,输出 "Main Thread continues."。 CountDownLatch
的计数器初始值为2,每个子线程执行完毕后通过 countDown()
将计数器减一,当计数器减到零时,主线程被唤醒。
在多线程环境下,确保数据一致性是非常重要的,因为多个线程可能同时访问和修改共享的数据。以下是一些常见的方法来实现多个线程之间的参数传递和数据一致性:
使用线程安全的数据结构: 选择线程安全的数据结构,例如
ConcurrentHashMap
、CopyOnWriteArrayList
等,以确保在多个线程同时访问时不会发生数据不一致的情况。使用同步机制: 使用
synchronized
关键字或ReentrantLock
进行同步,确保在一个线程访问共享数据时,其他线程不能同时访问。使用
volatile
关键字: 如果一个变量被volatile
修饰,那么当一个线程修改了这个变量的值,其他线程能立即看到修改后的值,从而确保数据的可见性。使用
Atomic
类:java.util.concurrent.atomic
包中提供了一系列的原子类,如AtomicInteger
、AtomicLong
等,它们提供了一些原子性操作,可以在不使用synchronized
的情况下实现对变量的安全访问。使用
ThreadLocal
: 如果每个线程需要拥有自己的变量副本,可以使用ThreadLocal
来存储线程本地变量,从而避免线程间的数据共享问题。使用
CountDownLatch
、Semaphore
等同步工具: 这些工具可以帮助线程之间进行同步,确保在特定的条件下线程能够按照期望的顺序执行。使用消息队列: 通过消息队列可以实现线程间的异步通信,一个线程将数据放入消息队列,其他线程从队列中取出数据进行处理。
注意,选择合适的方法取决于具体的应用场景和需求。在设计多线程应用时,必须仔细考虑数据共享和同步的问题,以确保程序的正确性和性能。