你好,我可以帮你找到一些关于Android多线程的信息。以下是一些关于Android多线程的文章,希望对你有所帮助:
- Android开发中多线程的几种模式
- 安卓多线程(10)——Handler与Looper
- Android开发之多线程编程(一)——Thread和Runnable接口
这些文章都详细介绍了Android多线程的基本概念、使用方法和注意事项。如果你有其他问题或需要更多帮助,请告诉我。
以下是根据提供的内容重构后的答案:
1. 耗时操作(如网络请求、I/O操作等)在计算机中的作用是什么?它们之间有什么区别?
耗时操作是指在计算机中执行时间较长的操作,如网络请求、I/O操作等。这些操作通常会导致程序的响应速度变慢,因为它们需要花费一定的时间来完成。在多线程编程中,耗时操作可能会导致线程阻塞,影响程序的执行效率。
守护线程与非守护线程的区别主要在于虚拟机是否已退出。当所有用户线程结束时,守护线程也会终止,而虚拟机同样退出;反过来,只要任何用户线程还在运行,守护线程就不会终止,虚拟机也不会退出。此外,守护线程的生命周期依赖于JVM,而非守护线程则有自己独立的生命周期。
2. 请简述Java中的线程优先级以及如何设置。
Java中的线程优先级分为10个级别,分别用Thread类常量表示。例如,Thread.MIN_PRIORITY表示优先级1,Thread.MAX_PRIORITY表示优先级10。默认线程优先级是5,即Thread.NORM_PRIORITY。通过方法setPriority(int grade)可以进行优先级设置。
3. 请简要介绍多线程编程以及其在实际应用中的作用。
多线程编程是指在同一时间内运行多个线程以提高程序的执行效率。在实际应用中,多线程可以帮助我们实现并发处理任务、提高系统性能等功能。然而,需要注意的是,由于CPU资源有限,因此多线程并不能完全解决性能问题。相反,它可能导致死锁、资源竞争等问题。
4. 请简述Java中的线程调度及其调度优先级。
Java中的线程调度采用时间片轮转的方式进行调度。当系统存在大量线程时,处于就绪状态(Runnable)的线程都会进入到线程队列中等待CPU资源。同一时刻在线程队列中可能有很多个线程。在采用时间片的系统中,每个线程都有机会获得CPU的资源以便进行自身的线程操作;当线程使用CPU资源的时间到后,即时线程没有完成自己的全部操作,JVM也会中断当前线程的执行,把CPU资源的使用权切换给下一个队列中等待的线程。被中断的线程将等待CPU资源的下一次轮回,然后从中断处继续执行。
Java虚拟机(JVM)中的线程调度器负责管理线程,并根据线程优先级(高-低)和具备相同优先级的线程以轮流的方式获取CPU资源进行调度。例如,存在A、B、C、D四个线程,其中:A和B的优先级高于C和D(A、B同级,C、D同级),那么JVM将先以轮流的方式调度A、B,直到A、B线程死亡,再以轮流的方式调度C、D。
以下是重构后的代码:
```java
public void run() {
// 定义的线程行为
}
}
// 步骤3:创建线程对象,即实例化线程类
MyThread mt = new MyThread("线程名称");
// 步骤4:通过线程对象控制线程的状态,如运行、睡眠、挂起/停止
// 此处采用start()开启线程
mt.start();
// 配合Runnable接口创建线程
// 步骤1:创建线程辅助类,实现Runnable接口
class MyThread implements Runnable {
....
@Override
// 步骤2:复写run(),定义线程行为
public void run() {
}
}
// 步骤3:创建线程辅助对象,即实例化线程辅助类
MyThread mt = new MyThread();
// 步骤4:创建线程对象,即实例化线程类;线程类 = Thread类;
// 创建时通过Thread类的构造函数传入线程辅助类对象
// 原因:Runnable接口并没有任何对线程的支持,我们必须创建线程类(Thread类)的实例,从Thread类的一个实例内部运行
Thread td = new Thread(mt);
// 步骤5:通过线程对象控制线程的状态,如运行、睡眠、挂起/停止
// 当调用start()方法时,线程对象会自动回调线程辅助类对象的run(),从而实现线程操作
td.start();
```
调用new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。这不利于扩展,比如定时执行、定期执行和线程中断等任务。
为了解决这个问题,我们可以使用Handler来实现在多线程的应用场景中,将工作线程中需更新UI的操作信息传递到UI主线程,从而实现工作线程对UI的更新处理,最终实现异步消息的处理。Handler是一套Android消息传递机制,它与Looper和Message这三者都与Android异步消息处理线程相关的概念。
什么是异步消息处理线程呢?异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。
Android消息机制主要是指Handler的运行机制,Handler运行需要底层的MessageQueue和Looper支撑。其中MessageQueue采用的是单链表的结构,Looper可以叫做消息循环。由于MessageQueue只是一个消息存储单元,不能去处理消息,而Looper就是专门来处理消息的,Looper会以无限循环的形式去查找是否有新消息,如果有的话,就处理,否则就一直等待着。
我们知道,Handler创建的时候会采用当前线程的Looper来构造消息循环系统。需要注意的是,线程默认是没有Looper的,如果需要使用Handler就必须为线程创建Looper。因为默认的UI主线程,也就是ActivityThread,ActivityThread被创建的时候就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。如果想让该线程具有消息队列和消息循环,并具有消息处理机制,就需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。
以下是一个示例代码:
```java
public class LopperThread extends Thread {
public Handler mHandler;
@Override
public void run() {
Looper.prepare();
mHandler = new Handler(Looper.myLooper());
}
}
```
重构后的代码如下:
```java
// 创建Handler实例并初始化
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 处理消息队列中的任务并更新UI
}
};
// 在主线程中启动工作线程(同时启动了Handler)
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
mHandler.sendMessage(mHandler.obtainMessage()); // 将消息放入消息队列
Looper.loop();
}
});
thread.start();
```
2.5 使用方式:
在Android中,我们可以通过两种方式来发送消息给Handler。第一种是新建一个内部类或匿名内部类的方式,第二种是通过sendMessage方法。下面是两种方式的详细解释和示例代码。
**方式1:新建Handler子类(内部类)**
首先,我们需要自定义一个Handler的子类,并重写handleMessage方法来确定更新UI的操作。然后在主线程中创建这个Handler的实例,并实例化一个消息对象,设置消息的标识符和内容,最后通过handler的sendMessage方法将消息发送到消息队列中。如果需要启动一个新的线程来处理这些消息,可以使用AsyncTask、继承Thread类或者实现Runnable接口。
```java
// 第一种方式:新建Handler子类(内部类)
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 这里编写处理消息队列中的任务并更新UI的代码
}
}
// 然后在主线程中创建MyHandler实例,并实例化消息对象,最后发送消息到消息队列中
// 注意:这里使用的是匿名内部类的方式来创建MyHandler实例,你也可以选择新建一个内部类的方式来创建MyHandler实例
MyHandler myHandler = new MyHandler() {};
Message message = Message.obtain(); // 实例化消息对象
message.what = 1; // 设置消息的标识符
message.obj = "AA"; // 设置消息的内容存放位置
myHandler.sendMessage(message); // 将消息发送到消息队列中
```
public void handleMessage(Message msg) {
...// 需执行的UI操作
}
};
// 步骤2:创建消息对象
Message msg = Message.obtain(); // 实例化消息对象
msg.what = 1; // 消息标识
msg.obj = "AA"; // 消息内容存放
// 步骤3:在工作线程中 通过Handler发送消息到消息队列中
// 多线程可采用AsyncTask、继承Thread类、实现Runnable
mHandler.sendMessage(msg);
// 步骤4:开启工作线程(同时启动了Handler)
// 多线程可采用AsyncTask、继承Thread类、实现Runnable
Handler.post(new Runnable() {
@Override
public void run() {
... // 需执行的UI操作
}
});
// 步骤1:在主线程中创建Handler实例
private Handler mhandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
handleMessage(msg);
}
};
// 步骤2:在工作线程中 发送消息到消息队列中 & 指定操作UI内容
// 需传入1个Runnable对象
mHandler.post(new Runnable() {
@Override
public void run() {
... // 需执行的UI操作
}
});
// 步骤3:开启工作线程(同时启动了Handler)
// 多线程可采用AsyncTask、继承Thread类、实现Runnable
为了解决Handler中消息队列中的所有消息都能被执行,同时避免内存泄漏问题,可以使用静态内部类和弱引用的方式。以下是修改后的代码:
```java
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
private Handler showhandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1. 实例化自定义的Handler类对象
// a. 此处并无指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue;
// b. 定义时需传入持有的Activity实例(弱引用)
showhandler = new FHandler(this);
// 2. 启动子线程
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; // 消息标识
msg.obj = "BB"; // 消息存放
// b. 传入主线程的Handler并向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
}
}
```
解决方案1:自定义Handler子类
```java
private static class FHandler extends Handler {
// 定义弱引用实例
private WeakReference
// 在构造方法中传入需持有的Activity实例
public FHandler(Activity activity) {
// 使用WeakReference弱引用持有Activity实例
reference = new WeakReference<>(activity);
}
// 通过复写handlerMessage() 从而确定更新UI的操作
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
break;
case 2:
break;
}
}
}
```
解决方案2:当外部类结束生命周期时,清空Handler内消息队列
```java
@Override
protected void onDestroy() {
super.onDestroy();
// 当外部类Activity生命周期结束时,同时清空消息队列并结束Handler生命周期
mHandler.removeCallbacksAndMessages(null);
}
```
解决方案3:AsyncTask(Handler+线程池(默认串行))
1、在工作线程中执行任务,如耗时任务。
2、实现工作线程与主线程(UI线程)之间的通信,即将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作。
3、从而保证线程安全。
3.1 优点:方便实现异步通信,不需使用“任务线程(如继承Thread类)+ Handler”的复杂组合,节省资源,采用线程池的缓存线程 + 复用线程,避免了频繁创建 & 销毁线程所带来的系统资源开销。
3.2 缺点:AsyncTask是非静态内部类,需要创建对象才能调用其方法。
实现一个AsyncTask比较繁琐,而且往往不用的业务需要实现不同的AsyncTask,导致代码可读性差一点。实际上项目用得不多,有其他更好的替代方案。
以下是一个简化后的示例:
```java
public class MainActivity extends AppCompatActivity {
// 线程变量
private MyTask mTask;
// 主布局中的UI组件
Button button, cancel; // 加载、取消按钮
TextView text; // 更新的UI组件
ProgressBar progressBar; // 进度条
/**
* 步骤1:创建AsyncTask子类
*/
private class MyTask extends AsyncTask
// 方法1:onPreExecute()
// 作用:执行线程任务前的操作
@Override
protected void onPreExecute() {
text.setText("加载中");
// 执行前显示提示
}
// 方法2:doInBackground()
// 作用:接收输入参数、执行任务中的耗时操作、返回线程任务执行的结果
// 此处通过计算从而模拟“加载进度”的情况
@Override
protected String doInBackground(String... params) {
try {
int count = 0;
int length = 1;
while (count < 99) {
count += length;
// 可调用publishProgress()显示进度,之后将执行onProgressUpdate()
publishProgress(count);
// 模拟耗时任务
}
} catch (Exception e) {
e.printStackTrace();
}
return "任务完成";
}
}
```
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
// 方法3:onProgressUpdate()
// 作用:在主线程中显示线程任务执行的进度
@Override
protected void onProgressUpdate(Integer... progresses) {
progressBar.setProgress(progresses[0]);
text.setText("loading..." + progresses[0] + "%");
}
// 方法4:onPostExecute()
// 作用:接收线程任务执行结果,并将执行结果显示到UI组件上
@Override
protected void onPostExecute(String result) {
// 任务执行完毕后,更新UI组件
text.setText("加载完毕");
}
// 方法5:onCancelled()
// 作用:将异步任务设置为取消状态
@Override
protected void onCancelled() {
text.setText("已取消");
progressBar.setProgress(0);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 绑定UI组件
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
cancel = (Button) findViewById(R.id.cancel);
text = (TextView) findViewById(R.id.text);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
AsyncTask子类的实例必须在UI线程中创建。以下是重构后的代码:
```java
// 步骤2:创建AsyncTask子类的实例对象(即任务实例)
mTask = new MyTask();
// 加载按钮按下时,启动AsyncTask
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 步骤3:手动调用execute(Params... params)以执行异步线程任务
// 注:
// a. 必须在此线程中调用
// b. 同一个AsyncTask实例只能执行一次,若执行第二次将抛出异常
// c. 在执行任务过程中,系统会自动调用AsyncTask的一系列方法:onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute()
// d. 不能手动调用上述方法
mTask.execute();
}
});
cancel = (Button) findViewById(R.id.cancel);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 取消正在执行的任务,onCancelled方法将被调用
mTask.cancel(true);
}
});
}
}
```
以下是重构后的代码:
```java
// 步骤1:创建HandlerThread实例对象,传入线程名字,标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步骤2:启动线程
mHandlerThread.start();
// 步骤3:创建工作线程Handler并复写handleMessage()方法,关联HandlerThread的Looper对象、实现消息处理操作并与其他线程进行通信
// 注:消息处理操作(handleMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
Handler workHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public boolean handleMessage(Message msg) {
// ...消息处理。因为这里是线程中的Looper,是可以做异步延迟的
try {
//延时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 这里不能直接更新UI,如果想要更新UI必须获取到主线程的Handler,才能更新
return true;
}
};
// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息,在工作线程中当消息循环时取出对应消息并在该工作线程执行相关操作
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
// 步骤5:结束线程,即停止线程的消息循环
mHandlerThread.quit();
```
在Android中,使用Handler时需要注意内存泄露的问题。如果不注意,可能会导致内存泄漏。当Handler类的实例不是静态的时,就可能发生这种情况。
为了避免这种情况,可以使用静态内部类来实现Handler。这样可以确保在使用完Handler后,它会被正确地回收。
此外,还可以使用IntentService来处理后台任务。当需要在后台执行一些耗时操作时,可以使用IntentService。IntentService是Service的子类,可以根据需要处理异步请求(以intent表示)。客户端通过调用startService(Intent)发送请求,该Service根据需要启动,使用工作线程处理依次每个Intent,并在停止工作时停止自身。
使用IntentService的步骤如下:
1. 定义一个IntentService的子类,并复写onHandleIntent()方法。这个方法将在工作线程中执行耗时操作。
```java
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
// 根据 Intent的不同,进行不同的事务处理
String taskName = intent.getExtras().getString("taskName");
}
}
```
2. 在需要的地方启动MyIntentService。例如,可以在Activity中启动它:
```java
startService(new Intent(this, MyIntentService.class));
```
3. 当不再需要MyIntentService时,可以将其停止。例如,可以在Activity中调用stopService(new Intent(this, MyIntentService.class)):
```java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private MyIntentService myIntentService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// 在需要的地方开启服务
public void startMyIntentService() {
if (myIntentService == null) {
Intent intent = new Intent(this, MyIntentService.class);
startService(intent);
} else if (myIntentService.isBinded()) {
Log.d(TAG, "service is started");
} else {
Log.d(TAG, "service is not started or service already stop");
}
}
// 在需要的地方停止服务并销毁实例
public void stopMyIntentService() {
if (myIntentService != null) {
myIntentService.removeUpdates();
myIntentService.stopSelf();
} else {
Log.d(TAG, "service is not bound to activity");
}
}
}
```
```java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 同一服务只会开启1个工作线程
// 在onHandleIntent()函数里,依次处理传入的Intent请求
// 将请求通过Bundle对象传入到Intent,再传入到服务里
// 请求1
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
// 请求2
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
}
}
```
线程池的核心参数如下:
1. corePoolSize:线程池的核心线程数,默认情况下,核心线程数会一直在线程池中存活,即使它们处理闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会执行超时策略,这个时间间隔由keepAliveTime所指定,当等待时间超过keepAliveTime所指定的时长后,核心线程就会被终止。
2. maximumPoolSize:线程池所能容纳的最大线程数量,当活动线程数到达这个数值后,后续的新任务将会被阻塞。如果这个无限大永远不会阻塞,除非开辟的线程超过了CPU承受的最大范围。
3. keepAliveTime:非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长。
4. unit:keepAliveTime这个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等。常用的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)等。
5. workQueue:线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。
6. threadFactory:为线程池提供创建新线程的功能,这个我们一般使用默认即可。
7. handler:拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。
以下是一个创建和使用线程池的示例:
```java
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 1. 创建线程池
ExecutorService threadPool = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue
);
// 2. 向线程池提交任务:execute()
for (int i = 0; i < 20; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("Task " + index + " is running by " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
// 3. 关闭线程池(注意:这里没有调用shutdown(),因为我们希望保持任务队列中的任务继续执行)
threadPool.shutdown(); // 如果不再提交新任务,可以调用此方法关闭线程池并等待所有已提交任务执行完毕
}
}
```
线程池的使用和关闭
线程池是一种管理线程的工具,它可以在需要时创建新线程,也可以在完成任务后回收线程。使用线程池可以提高系统性能,避免频繁创建和销毁线程带来的开销。下面介绍如何使用线程池以及如何关闭线程池。
1. 创建线程池
```java
// 创建固定大小的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 创建可缓存的线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 创建单任务线程池
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
```
2. 提交任务到线程池
```java
threadPool.execute(new Runnable() {
@Override
public void run() {
// 线程执行任务
}
});
```
3. 关闭线程池
```java
threadPool.shutdown();
```
关闭线程池的方法有两种:shutdown()和shutdownNow()。它们的区别如下:
- shutdown():设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程。这种方式会等待所有任务执行完毕后才关闭线程池。
- shutdownNow():设置线程池的状态为STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。这种方式会立即停止所有任务,不等待它们执行完毕。
根据实际需求选择合适的方法关闭线程池。如果任务不一定要执行完,可以使用shutdownNow()方法;如果需要等待所有任务执行完毕后再关闭线程池,可以使用shutdown()方法。
Timer的特点:
1. Timer是单线程模式,即只有一个线程负责执行任务;
2. 如果在执行任务期间某个TimerTask耗时较久,那么就会影响其它任务的调度;
3. Timer的任务调度是基于绝对时间的,对系统时间敏感;
4. Timer不会捕获执行TimerTask时所抛出的异常,由于Timer是单线程,所以一旦出现异常,则线程就会终止,其他任务也得不到执行。
ScheduledThreadPoolExecutor的特点:
1. ScheduledThreadPoolExecutor是多线程,可以同时执行多个任务;
2. 多线程,单个线程耗时操作不会影响影响其它任务的调度;
3. 基于相对时间,对系统时间不敏感;
4. 多线程,单个任务的执行异常不会影响其他线程。
创建定时线程池对象并设置线程池线程数量固定为5:
```java
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
```
创建好Runnable类线程对象需执行的任务:
```java
Runnable task = new Runnable() {
public void run() {
System.out.println("执行任务啦");
}
};
```
向线程池提交任务:schedule()
```java
scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
scheduledThreadPool.scheduleAtFixedRate(task, 10, 1000, TimeUnit.MILLISECONDS); // 延迟10ms后、每隔1000ms执行任务
```
关闭线程池:
```java
scheduledThreadPool.shutdown();
```
可缓存线程池(CachedThreadPool)的特点:
1. 只有非核心线程、线程数量不固定(可无限大)、灵活回收空闲线程(具备超时机制,全部回收时几乎不占系统资源)、新建线程(无线程可用时),不会阻塞;
2. 应用场景:执行大量、耗时少的线程任务。
创建可缓存线程池对象:
```java
ThreadPoolExecutor cachedThreadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue
```
您好,根据您的要求,我完成了内容重构并保持了段落结构。以下是重构后的内容:
6.2 缓存线程池(CachedThreadPool)
特点:创建和销毁线程的开销小,适合执行大量短暂任务的场景。
应用场景:适合执行大量短暂任务,如网络请求、文件读写等。
6.2.1 创建缓存线程池
```java
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:execute()
cachedThreadPool.execute(task);
// 4. 关闭线程池
cachedThreadPool.shutdown();
```
当执行第二个任务时第一个任务已经完成,那么会复用执行第一个任务的线程,而不用每次新建线程。
6.2.4 单线程化线程池(SingleThreadExecutor)
特点:只有一个核心线程(超过一个任务会阻塞,保证所有任务按照指定顺序在一个线程中执行,不需要处理线程同步的问题)
应用场景:不适合并发但可能引起IO阻塞性及影响UI线程响应的操作,如数据库操作,文件操作等
6.3 线程池的注意点
虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。用线程池构建的应用程序容易遭受任何其它多线程应用程序容易遭受的所有并发风险,诸如同步错误和死锁,它还容易遭受特定于线程池的少数其它风险,诸如与池有关的死锁、资源不足和线程泄漏。
线程池的缺点包括:需要合理配置,线程池的性能和效果受到配置参数的影响,需要根据具体的应用场景和硬件环境来合理配置线程池的大小、任务队列的大小等参数;如果线程池太大,那么被那些线程消耗的资源可能严重地影响系统性能。在线程之间进行切换将会浪费时间,而且使用超出比您实际需要的线程可能会引起资源匮乏问题,因为池线程正在消耗一些资源,而这些资源可能会被其它任务更有效地利用;除了线程自身所使用的资源以外,服务请求时所做的工作可能需要其它资源,例如 JDBC 连接、套接字或文件。这些也都是有限资源,有太多的并发请求也可能引起失效,例如不能分配 JDBC 连接;线程池和其它排队机制依靠使用 wait() 和 notify() 方法,这两个方法都难于使用。如果编码不正确,那么可能丢失通知,导致线程保持空闲状态,尽管队列中有工作要处理。使用这些方法时,必须格外小心;即便是专家也可能在它们上面出错。
线程池中存在多种风险,其中一种严重的问题是线程泄漏。当从池中取出一个线程执行任务后,如果该线程在任务完成后没有返回到池中,就会发生线程泄漏。这种风险在某些情况下可能会加剧,例如当任务抛出运行时异常(RuntimeException)或错误(Error)时。如果线程池的类没有捕获这些异常,那么只有退出的线程会被回收,导致线程池的大小永久减少一个。
然而,有些任务可能会永远等待某些资源或用户输入,而这些资源无法保证随时可用,用户也有可能已经离线。这样的任务会导致线程池中的线程永久地消耗掉,从而导致线程泄漏的问题。对于这类任务,我们应该考虑使用独立的线程来处理它们,或者设置一个有限的等待时间。
此外,请求过载也是线程池面临的风险之一。在这种情况下,服务器可能无法承受来自客户端的大量请求,因为排队等待执行的任务会消耗过多的系统资源并引发资源短缺。面对请求过载的情况,您可以根据实际情况采取不同的应对策略。例如,您可以简单地拒绝这些请求,让请求者稍后再重试;或者使用一个表示服务器暂时繁忙的响应来告知用户。