1. 线程池概念
现有问题:
线程是宝贵的内存资源、单个线程约占1MB的空间,过多分配易造成内存溢出;
频繁的创建及销毁会增加虚拟机回收频率、资源开销,造成程序性能下降。
线程池:
线程容器,可设定线程分配的数量上限;
将预先创建的线程对象存入池中,并重用线程池中的线程对象;
避免频繁的创建和销毁。
2. 线程池原理
将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程。
Java线程池
3. 获取线程池 - Executor接口 & Executors工具类
常用的线程池接口和类:
① Executor // 接口,线程池的顶级接口
② ExecutorService //接口,线程池接口,可通过Future> submit(Runnable task)提交任务代码
③ Executors // 工厂类,通过此类可以获得一个线程池
(1)通过 Executors.newFixedThreadPool(int nThreads) 获取固定数量的线程池
@nThreads int 指定线程池中线程的数量
@return ExecutorService 接口类型引用
(2)通过 Executors.newCachedThreadPool()获得动态数量的线程池,如不够则创建新的,没有上限
@return ExecutorService 接口类型引用
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestThreadPool {
public static void main(String[] args) {
// 线程池引用 --> 指向Executors工具类(工厂类)
// 手动限定线程池中的固定数量
//ExecutorService es = Executors.newFixedThreadPool(3);
// 动态数量的线程池
ExecutorService es = Executors.newCachedThreadPool();
// 1.创建任务类对象
MyTask task = new MyTask();
// 2.将任务提交到线程池,由线程池调度、执行(谁先运行结束则优先被复用)
es.submit(task);
es.submit(task);
es.submit(task);
es.submit(task);
es.submit(task);
es.submit(task);
}
}
// 线程任务
class MyTask implements Runnable {
public void run() {
int i = 0;
while (i++ < 10) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
4. Callable接口
public interface Callable {
public V call() throws Exception;
}
特点:
JDK5加入,与Runnable接口类似,实现之后代表一个线程任务;
Callable具有泛型返回值,可以声明异常。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestCallable {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(3);
ThisTask task = new ThisTask();
es.submit(task);
}
}
class ThisTask implements Callable {
@Override
public Object call() throws Exception {
for (int i = 1; i <= 50; i++) {
Thread.sleep((int)(Math.random() * 1000));
System.out.println(Thread.currentThread().getName() + ":" + i);
}
return null;
}
}
5. Future接口
public interface Future
特点:
异步接收ExecutorService.submit()所返回的状态结果,当中包含了call()的返回值;
方法:V get()以阻塞形式等待Future中的异步处理结果(call()的返回值)
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;
public class TestFuture {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es = Executors.newFixedThreadPool(3);
MyCall call = new MyCall();
MyCall2 call2 = new MyCall2();
// 通过submit执行提交的任务,Future接收返回的结果
Future f = es.submit(call);
Future f2 = es.submit(call2);
System.out.println( f.get() + f2.get()); // 5050
}
}
// 计算1~50的和
class MyCall implements Callable {
@Override
public Integer call() throws Exception {
Integer sum = 0;
for (int i = 1; i <= 50; i++) {
sum += i;
}
System.out.println(Thread.currentThread().getName() + ":" + sum);
return sum; // 1275
}
}
// 计算51~100的和
class MyCall2 implements Callable {
@Override
public Integer call() throws Exception {
Integer sum = 0;
for (int i = 51; i <= 100; i++) {
sum += i;
}
System.out.println(Thread.currentThread().getName() + ":" + sum);
return sum; // 3775
}
}
6. 线程的异步与同步
同步:
形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续。
同步为单条执行路径。
异步:
形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回,二者竞争时间片,并发执行。
异步为多条执行路径。