1、多线程简介
- 线程是程序中一个单一的顺序控制流程;而多线程就是在单个程序中同时运行多个线程来完成不同的工作。
- 多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。多线程是在同一时间需要完成多项任务的时候实现的。
1、概念:
- 线程就是独立的执行路径
- 在线程运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
- main()称之为主线程,为系统的入口,用于执行整个程序;
- 在一个进程中,如果开辟了多个线程,线程的运行有调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的;
- 对同一个资源操作时,会存在资源抢夺的问题,需要加入并发控制;
- 线程会带来额外的开销,如cpu调度时间,并发控制开销;
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致;
2、多线程的优缺点
优点:
1)、多线程技术可以加快程序的运行速度,使程序的响应速度更快,因为用户界面可以在进行其它工作的同时一直处于活动状态
2)、可以把占据长时间的程序中的任务放到后台去处理,同时执行其他操作,提高效率
3)、当前没有进行处理的任务时可以将处理器时间让给其它任务
4)、可以让同一个程序的不同部分并发执行,释放一些珍贵的资源如内存占用等等
5)、可以随时停止任务
6)、可以分别设置各个任务的优先级以优化性能
缺点:
1)、因为多线程需要开辟内存,而且线程切换需要时间因此会很消耗系统内存。
2)、线程的终止会对程序产生影响
3)、由于多个线程之间存在共享数据,因此容易出现线程死锁的情况
4)、对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。
2、线程创建
三种创建方式
- Thread class 继承Thread类(重点)
- Runnable接口 实现Runnable接口(重点)
- Callable接口 实现Callable接口(了解)
1、重写Thread的Run方法
- 自定义线程类继承Thread类
- 重写Run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
例子:
// 创建线程方式一 : 继承Thread类 , 重新run()方法,调用start开启线程
public class TestThread1 extends Thread{
@Override
public void run() {
// run方法线程题
for (int i = 0; i < 200; i++) {
System.out.println("我在看电影==="+i);
}
}
public static void main(String[] args) {
// main线程,主线程
TestThread1 testThread1 = new TestThread1(); // 创建一个线程对象
testThread1.start(); // 调用start()方法开启线程
for (int i = 0; i < 2000; i++) {
System.out.println("我在学习==="+i);
}
}
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ay5vFyrj-1607676983716)(img/image-20201208192142609.png)]
ps: 线程 开启不一定立即执行,有CPU调度执行
2、通过Runnable的run()方法 (推荐使用)
- 自定义线程类实现接口Runnable接口类
- 重写Run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
// 创建线程方式2:实现runnable接口,重写run方法,执行线程需要Runnable接口实现类,调用start方法
public class TestThread3 implements Runnable{
@Override
public void run() {
// run方法线程题
for (int i = 0; i < 200; i++) {
System.out.println("我在看电影==="+i);
}
}
public static void main(String[] args) {
// 创建runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();
// 创建线程对象,通过线程对象来开启我们的线程
// Thread thread = new Thread(testThread3);
// thread.start();
new Thread(testThread3).start();
for (int i = 0; i < 2000; i++) {
System.out.println("我在学习==="+i);
}
}
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bISVQz4m-1607676983719)(img/image-20201208194621198.png)]
ps : 第一种方法是继承Thread类,第二种方法是实现Runnable接口 都是实现run方法
3、实现Callable接口(了解即可)
-
实现Callable接口,需要返回值类型
// 线程创建方式三:实现Callable接口 public class TestCallable implements Callable<Boolean> { }
-
重写call方法,需要抛出异常
@Override public Boolean call() { return true; }
-
创建目标对象
-
创建执行服务
// 创建执行服务 // 3表示有三个线程 ExecutorService service = Executors.newFixedThreadPool(3);
-
提交执行
// 提交执行 Future<Boolean> result1 = service.submit(t1); Future<Boolean> result2 = service.submit(t2); Future<Boolean> result3 = service.submit(t3);
-
获取结果
// 获取结果 boolean r1 = result1.get(); boolean r2 = result2.get(); boolean r3 = result3.get();
-
关闭服务
// 关闭服务 service.shutdownNow();
ps: Callable的好处 可以定义返回值 可以抛出异常
3、理解Thread类
-
Thread类实现了Runnable接口
Runnable接口类
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
Thread实现类 实现了Runnable类
public class Thread implements Runnable{ /* What will be run. */ private Runnable target; /** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } } }
-
也就是说Thread是代理对象
他可以代理很多Runnable做不了的事情
-
Thread代理了Runnable真实对象
-
案例
public class StaticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
// 你去结婚
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("我要结婚了");
}
}
// 代理对象,帮助你结婚
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void before() {
System.out.println("布置现场");
}
private void after() {
System.out.println("收尾款");
}
}
4、线程的状态
1、线程的五大状态分别是:
- new : 新建状态
- Runnable:就绪状态
- Running:运行状态
- Blocked:阻塞状态
- Dead:死亡状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v3M1yDtA-1607676983720)(img/image-20201210095646636.png)]
2、线程的常用方法
方法 | 说明 |
---|---|
setProiority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 等待该线程停止 |
static void yield() | 暂定当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程,(不推荐用这个方式) |
boolean isAlive() | 测试线程是否处于活动状态 |
3、停止线程
官方的JDK已经不推荐使用stop()、destory()方法;
这里使用标志位停止线程;也就是一个布尔变量;
案例:
package com.ziji.state;
// 测试stop
public class TestStop implements Runnable{
// 1、设置一个变量
boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("sun....Thread"+i++);
}
}
// 2、设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i==900){
// false停止
testStop.stop();
System.out.println("停止");
}
}
}
}
4、线程休眠(sleep)
- sleep(时间)指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等。
- 每个对象都有一把锁,sleep不会释放锁
例子:
// 模拟倒计时
public class TestSleep2 {
public static void main(String[] args) throws InterruptedException {
// 打印当前系统时间
Date startTime = new Date(System.currentTimeMillis());// 获取当前系统时间
while (true) {
Thread.sleep(1000); // 延时1秒
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());// 更新当前系统时间
}
}
// 模拟倒计时
public static void tenDown() throws InterruptedException {
int num = 10;
while (true) {
Thread.sleep(1000);
System.out.println(num--);
if(num<=0){
break;
}
}
}
}
5、线程礼让(yield)
-
礼让线程,让当前线程
-
将线程从运行状态转为就绪状态
-
让cpu重新调度,礼让不一定成功!
-
也就是说 线程A让先程B 就相当于重新执行一遍 所以不一定成功!
例子:
// 测试礼让线程 public class TestYield { public static void main(String[] args) { MyYield myYield = new MyYield(); new Thread(myYield,"a").start(); new Thread(myYield,"b").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始执行"); Thread.yield(); // 礼让 System.out.println(Thread.currentThread().getName()+"线程停止执行"); } }
6、线程强制执行(join)
-
也就是插队
-
例子:
// 测试Join方法 插队 public class TestJoin implements Runnable{ @Override public void run() { for (int i = 1; i <= 10; i++) { System.out.println("插队的线程"+i); } } public static void main(String[] args) throws InterruptedException { // 启动线程 TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); // 主线程 for (int i = 1; i <= 50; i++) { if(i==10){ thread.join(); // 插队 执行插队的线程 } System.out.println("主线程"+i); } } }
7、观察线程状态
-
利用Thread里面的getState()方法
-
例子:
// 观察状态 public class TestState { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("main方法执行完毕!!!"); }); // 观察状态 Thread.State state = thread.getState(); System.out.println(state); // 输出状态 NEW // 观察启动后 thread.start();// 启动线程 state = thread.getState(); System.out.println(state);// 观察启动后线程的状态 while (state != Thread.State.TERMINATED) {// 线程不终止,输出状态 Thread.sleep(1000); state = thread.getState();// 线程执行时的状态 System.out.println(state); } } }
结果:
NEW // 新生状态 RUNNABLE // 运行状态 TIMED_WAITING // 等待状态 TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING main方法执行完毕!!! TERMINATED // 停止状态
-
5、线程的优先级
1、概念
线程优先级,就跟买彩票一样,买的越多,概率就越大,并不是优先级越高,他就会被调用
线程优先级用数字表示,一共是1~10
线程自带的优先级(静态常亮)
- Thread.MIN_PRIORITY = 1 // 线程的最低优先级
- Thread.MAX_PROIORITY = 10; // 线程的最高优先级
- Thread.NORM_PRIORITY = 5; // 线程默认的优先级
2、设置优先级
-
新建一个主线程
-
new 一个主线程
-
创建一个Thread线程类 把主线程放进线程类里面
-
设置优先级
-
启动
-
案例
public class TestPriority { public static void main(String[] args) { // 第二步 MyPriority myPriority = new MyPriority(); // 第三步 Thread t1 = new Thread(myPriority,"线程1"); Thread t2 = new Thread(myPriority,"线程2"); // 第四步 t1.setPriority(1); t2.setPriority(10); // 第五步 t1.start(); t2.start(); } } // 第一步 class MyPriority implements Runnable{ @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"==>"+Thread.currentThread().getPriority()); } }
6、守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕(用户线程执行完毕,守护线程自动停止)
- 如后台记录操作日志,监控内存,垃圾回收等
public class TestDaemon {
public static void main(String[] args) {
Log log = new Log();
Login login = new Login();
Thread thread = new Thread(log); // 创建日志线程
thread.setDaemon(true); // 设置为守护线程
thread.start(); // 启动守护线程
thread = new Thread(login); // 创建用户登录操作
thread.start(); // 启动用户登录操作
}
}
/**
* 日志记录用户登录操作
*/
class Log implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("记录着用户登录");
}
}
}
/**
* 用户登录操作100次
*/
class Login implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("用户第"+i+"次登录");
}
}
}
ps:
thread.setDaemon(true)
// 设置为守护线程设置为守护线程 ,默认的是false ,用户线程
7、线程同步
多个线程操作同一个资源,会引起并发问题
1、并发问题
例子1:
// 多个线程同时操作同一个对象
// 买火车票的例子
public class TestThread4 implements Runnable{
// 票数
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums<=0) {
break;
}
// 模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"===>拿到了第"+ticketNums--+"张票");
}
}
public static void main(String[] args) {
TestThread4 ticket = new TestThread4();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛党").start();
}
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rZpw3Sqz-1607676983737)(img/image-20201208200017088.png)]
上面我们可以发现老师跟小明同时拿到了第10张票
ps: 多个线程操作同一个资源的情况下,线程不安全,数据紊乱
例子2:
// 线程不安全的集合
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QIhRQenQ-1607676983738)(img/image-20201211111857628.png)]
ps :可以发现我们循环了10000次,而控制台只显示9996次。线程同步问题,list循环的时候,其中有下标,执行了两次,覆盖了数据,导致数据丢失
2、解决并发问题
1、两种方法
-
队列(也就是让线程排好队,一个一个来)
-
锁(每个对象都有一把锁,正在执行的线程锁起来,线程执行完,再打开,其他的线程进入,依次类推,这样就保护了线程的安全性)
锁的关键字:
synchronized
存在的问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
3、使用锁
-
synchronized
写在方法上 (需要执行)// 多个线程同时操作同一个对象 // 买火车票的例子 public class TestThread4 implements Runnable{ // 票数 private int ticketNums = 10; @Override public synchronized void run() { // 只需要在执行方法上加一个synchronized即可 while (true) { if (ticketNums<=0) { break; } // 模拟延时 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"===>拿到了第"+ticketNums--+"张票"); } } public static void main(String[] args) { TestThread4 ticket = new TestThread4(); new Thread(ticket,"小明").start(); new Thread(ticket,"老师").start(); new Thread(ticket,"黄牛党").start(); } }
-
synchronized
代码块public class UnsafeList { public static void main(String[] args) { List<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized (list){ // 锁住要执行增删改的对象即可 list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
ps : synchronized默认锁的是本身,锁别的对象需要用
synchronized
代码块 也可以使用JUC包下的CopyOnWriteArrayList集合保存数据
4、死锁的情况
某个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题
例如:
// 死锁:
public class DeadLock {
public static void main(String[] args) {
get get1 = new get(0,"小明");
get get2 = new get(1,"小红");
get1.start();
get2.start();
}
}
// 狗
class Dog{
}
// 猫
class Cat{
}
class get extends Thread{
// 只有两个宠物
static Dog dog = new Dog(); // 宠物狗
static Cat cat = new Cat(); // 宠物猫
int choice; // 选择宠物狗还是选择猫
String name;// 谁领取宠物
public get(int choice, String name) {
this.choice = choice;
this.name = name;
}
@Override
public void run() {
// 领取宠物
try {
get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 领取宠物
private void get() throws InterruptedException {
if(choice == 0){
synchronized (dog){ // 领取宠物狗
System.out.println(this.name+"获取宠物狗");
Thread.sleep(1000);
synchronized (cat){
System.out.println(this.name+"获取宠物猫");
}
}
}else {
synchronized (cat){ // 领取宠物猫
System.out.println(this.name+"获取宠物猫");
Thread.sleep(1000);
synchronized (dog){
System.out.println(this.name+"获取宠物狗");
}
}
}
}
}
ps: 小明和小红去领养宠物,
小明领养完宠物狗时,小红领养完宠物猫
过了一秒小名又想领养宠物猫,而宠物猫已经被小红领养走,这样就会产生死锁的情况
5、解决死锁的方法:
一个线程里面只有一个资源
8、Lock锁
1、概念
在JDK1.5以后,在并发包(java.util.concurrent)里面添加包locks,并提供了Lock接口,用于与synchronized
类似的锁功能,不同的是Lock需要手动开启锁和释放锁。
2、使用方法
import java.util.concurrent.locks.ReentrantLock;
// 测试Lock锁
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}
}
class TestLock2 implements Runnable{
int ticketNums = 10;
// 定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();// 加锁
if (ticketNums > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
}else{
break;
}
}finally {
// 解锁
lock.unlock();
}
}
}
}
ps: 创建 private final ReentrantLock lock = new ReentrantLock(); Lock锁对象
开锁和关锁写在try catch块里
3、Lock锁的常用方法
- lock():加锁
- lockInterruptibly():可中断
- tryLock():尝试非阻塞的获取锁
- unlock():释放锁
4、synchronized
与lock
的区别
- Lock是显示锁(手动开启和关闭锁)synchronized是隐式锁,出了作用域自动释放锁
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好地扩展性(提供更多的子类)
9、线程通信
1、问题:
有两个线程A,B ,一个String[10]数组
A线程循环添加数据,添加满了,通知B线程更改数据。A线程等待
2、Java提供了几个方法解决线程之间的通信问题
- wait() : 一直等待
- wait(long timeout):指定等待的毫秒数
- notify() : 唤醒一个处于等待状态的线程
- notifyAll() : 唤醒同一个对象上所有等待方法的线程
3、两种解决方法
管程法
- 创建一个缓冲区
- 线程A 执行自己的添加方法
- 线程B 执行自己的更改方法
public class Test {
public static void main(String[] args) {
buffer buffer = new buffer();
new A(buffer).start();
new B(buffer).start();
}
}
// 线程A
class A extends Thread{
buffer buffer;
public A(buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程A添加第"+i+"次数据");
buffer.insert("abc");
}
}
}
// 线程B
class B extends Thread{
buffer buffer;
public B(buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程B更改第"+i+"次数据");
buffer.update();
}
}
}
// 缓冲区
class buffer{
String[] strings = new String[10];
int count = 0;
public synchronized void insert(String str){
if(count == str.length()){
// 线程A等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 容器没有满就添加
strings[count] = str;
count++;
// 通知线程B满了
this.notifyAll();
}
public synchronized void update(){
if(count == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果满了就--
strings[count] = "";
count--;
// 通知线程A没了
this.notifyAll();
}
}
信号灯法
通过boolean类型判断是true还是false , 来执行线程之间的通信
案例:
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
// 演员
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(i%2==0){
this.tv.play("哈哈哈哈哈播放中");
}else{
this.tv.play("奥特曼播放中");
}
}
}
}
// 观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
tv.watch();
}
}
}
// 节目
class TV{
String voice;// 表演的节目
boolean flag = true; // 表演为True ,观众为False
// 表演
public synchronized void play(String voice){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("表演了"+voice);
this.notifyAll(); // 通知唤醒
this.voice = voice;
this.flag = false;
}
// 观众
public synchronized void watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众正在观看");
this.notifyAll();
this.flag = true;
}
}
10、线程池
概念
- JDK 5.0起提供了线程池相关API: ExecutorService和Executors
- ExecutorService:真正的线程池接。常见子类ThreadPoolExecutor
- void execute(Kunnable command):执行任务/命令,没有返回值,一般用来执行Runnable
<T> Future<T> submit(Callable<T> task)
:执行任务,有返回值,一般又来执行Callable- void shutdown():关闭连接池
- Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
案例:
// 测试线程池
public class TestPool {
public static void main(String[] args) {
// 1、创建服务,创建线程池 newFixedThreadPool : 线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 2、关闭链接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println("这是一个线程"+Thread.currentThread().getName());
}
}
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众正在观看");
this.notifyAll();
this.flag = true;
}
}
# 10、线程池
### 概念
- JDK 5.0起提供了线程池相关API: ExecutorService和Executors
- ExecutorService:真正的线程池接。常见子类ThreadPoolExecutor
- void execute(Kunnable command):执行任务/命令,没有返回值,一般用来执行Runnable
- `<T> Future<T> submit(Callable<T> task)`:执行任务,有返回值,一般又来执行Callable
- void shutdown():关闭连接池
- Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
### 案例:
```java
// 测试线程池
public class TestPool {
public static void main(String[] args) {
// 1、创建服务,创建线程池 newFixedThreadPool : 线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 2、关闭链接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println("这是一个线程"+Thread.currentThread().getName());
}
}