线程是什么
1.一个程序在电脑中执行是会以进程的方式运行,此进程所能调用的资源就是程序能够调用的资源,然后在进程之下,会分化出许多线程,这些线程统一能够调用进程所持有的资源,线程是进程的分化,可以理想化的同时运行某些东西。
2.java中main方法是主线程,还会存在垃圾回收的线程,这两个线程是同时进行的,所以这种现象称为多线程。
Thread(类)
新知:
1.使用Thread类(线程同时启动)的步骤
1.继承Thread类,
重写类中的run方法,
方法中写需要和主线程main方法同步运行的代码
在mian方法中实例化继承自Thread类的对象,用对象调用start()方法启动分线程
2.线程执行顺序受操作系统调度器影响
启动分线程后,分线程与主线程的执行先后受到CPU的调度器影响,但无论如何两个线程不是顺序执行的,而是穿插执行,除 非某一个线程运行太快,来不及穿插执行
3.实现了接口Runnable
public class TextThread_01 extends Thread {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("我在学习多线程---"+i);
}
}
public static void main(String[] args) {
TextThread_01 thread_01=new TextThread_01();
//启动线程
thread_01.start();
for (int i = 0; i < 200; i++) {
System.out.println("我在学习main---"+i);
}
}
}
结果片段:
我在学习多线程---76
我在学习多线程---77
我在学习main---73
我在学习main---74
我在学习main---75
我在学习main---76
我在学习main---77
我在学习main---78
我在学习main---79
我在学习main---80
我在学习main---81
我在学习main---82
我在学习main---83
我在学习多线程---78
我在学习main---84
Runnable
1.是接口,一般情况优先用接口,扩展性高,
2.也是开启同步线程的方法
1.定义Runnable接口的实现类,实现run方法,然后在main方法中实例化类。
2.定义Thread类的实例化,并将上面的对象作为构造函数的参数传递
3.通过Thread的实例化变量调用start方法,启动线程
3.Thread.currentThread().getName():获得线程名
4.Thread.sleep(200);延时
public class TextRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("我在学习多线程---"+i);
}
}
public static void main(String[] args) {
//实例化类TextRunnable
TextRunnable runnable=new TextRunnable();
//2.定义Thread类的对象,把接口Runnable的实现类的实例化对象作为参数传递放入构造函数中
Thread thread=new Thread(runnable);
//调用start()方法启动线程
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println("我在学习main---"+i);
}
}
}
结果部分:
我在学习main---147
我在学习main---148
我在学习多线程---43
我在学习main---149
我在学习main---150
Callable
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rHGRLzUz-1599640746091)(Callale.png)]
代理模式
1.利用Runnable接口实现多线程时就是采用的代理模式。
Thread(new TextRunnable()).start();
Thread类帮助TextRunnable去实现一个线程的启动,不需要TextRunnable类自己手动的启动线程
Lambda:
对函数式接口的实现类简化
用处:比如说简化Runnable接口的实现
函数式接口
1.接口中只有一个需要实现的方法,这样的接口称函数式接口
//定义接口,只有一个需要实现的方法叫函数式接口
interface Love1{
public abstract void love1();
}
public class TextLambda {
public static void main(String[] args) {
//Lambda表达式
Love1 love=()-> System.out.println("这是函数式接口的lambda方法");
//匿名内部类方法
Love1 love1=new Love1(){
@Override
public void love1() {
System.out.println("这是匿名内部类方法");
}
};
love.love1();
love1.love1();
}
}
结果:
这是函数式接口的lambda方法
这是匿名内部类方法
线程状态
**创建线程(new)**:Thread t=new Thread后就代表线程进入了创建状态
就绪状态:当调用start()方法后,线程立即进入就绪状态,但是还需要等待操作系统的调度才能进入运行状态
**运行状态:**此状态下线程执行代码块
**阻塞状态:**当调用sleep,wait或者同步锁定时线程由运行状态进入到阻塞状态,代表线程中的代码不往下执行,只有等阻塞状态结束后,线程重写进入到就绪状态,等待重新调度
**结束状态(dead):**线程中断或者结束,一旦进入死亡状态,无法被重新启动
线程方法
1.等待这个线程死亡
void join()
2.sleep():暂停线程多少秒后继续执行
public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 线程不会丢失任何显示器的所有权。
3.yield()
static void yield()
对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。
4.setPriority():更改此线程的优先级
void setPriority(int newPriority)
更改此线程的优先级。
5.interrupt():中断这个线程
void interrupt()
中断这个线程。
6.isAlive() :测试这个线程是否活着。
boolean isAlive()
测试这个线程是否活着。
停止线程方法
使用标志位进行终止变量,当flag=false时,终止线程
步骤:
先在mmain方法中利用Lambda表达式实现Runnable接口中的方法,方法中写一个while循环,循环的判断条件是一个布尔型的变量且事先在类中定义好默认为true,再main方法下写一个for循环,i从0-6,当i等于3时,调用自定义的停止线程的方法,方法中就是将这个布尔型的标志位赋值为false,当while循环接收到新的标志位后会退出循环,以达到终止线程的目的。
public class MyStop {
//线程终止的标志位,通过对象引用变量更改flag的值为false来终止线程
boolean flag=true;
public static void main(String[] args) {
//只用new一个对象引用变量,只有这样在调用stop方法是才能影响到分线程中的while语句的判断
MyStop myStop = new MyStop();
//先写一个线程,利用Lambda表达式实现Runnable
Runnable runnable =()->{
int i=0;
while (myStop.flag){
System.out.println("线程正在运行:"+i++);
}
};
new Thread(runnable).start();//启动分线程
//写线程终止条件
for (int i = 0; i < 6; i++) {
if(i==3){
myStop.stop();//调用停止线程方法
System.out.println("线程即将终止");
}
System.out.println("Main:"+i);
}
}
// 写终止线程的方法
public void stop(){
this.flag=false;
}
}
结果:
线程正在运行:0
Main:0
线程正在运行:1
线程正在运行:2
线程正在运行:3
线程正在运行:4
线程正在运行:5
线程正在运行:6
线程正在运行:7
Main:1
线程正在运行:8
线程正在运行:9
线程正在运行:10
线程正在运行:11
线程正在运行:12
线程正在运行:13
Main:2
线程正在运行:14
线程正在运行:15
线程正在运行:16
线程即将终止
Main:3
Main:4
Main:5
线程休眠(sleep)
-
存在异常InterruptedException
-
static void sleep(long millis)
:参数单位:毫秒 -
每个对象都有一个锁,sleep不会释放锁
public class MyThreadSleep {
//模拟倒计时
public static int i=10;
public static void main(String[] args) throws InterruptedException {
while(i>=0){
Thread.sleep(1000);
System.out.println("倒计时:"+i--);
}
}
}
结果:
倒计时:10
倒计时:9
倒计时:8
倒计时:7
倒计时:6
倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1
倒计时:0
线程礼让(yield)
1.礼让不一定成功,
2.礼让是让当前正在运行的线程停止继续执行后面的代码块并进入就绪状态重新等待调度器的调度,重新和其他线程一起竞争,如果竞争成功,会继续执行后面的代码。
public class MyYield {
public static void main(String[] args) {
//定义多线程,用Lambda表达式
Runnable runnable=()->{
System.out.println(Thread.currentThread().getName()+"线程开始了");
//调用礼让方法
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止了");
};
new Thread(runnable,"a").start();
new Thread(runnable,"b").start();
}
}
结果:
b线程开始了
a线程开始了
a线程停止了
b线程停止了
线程强制执行(join)
强制打断当前线程执行特定线程
public class MyJoin {
public static void main(String[] args) throws InterruptedException {
Runnable runnable=()->{
for (int i = 0; i < 100; i++) {
System.out.println("我是vip"+i);
}
};
for (int i = 0; i < 100; i++) {
if(i==50){
Thread thread=new Thread(runnable);//main线程阻塞,分线程运行
thread.start();
thread.join();
}
System.out.println("我是main线程"+i);
}
}
}
观察线程状态
(可判断线程如果等待过度则停止线程的功能)
新知:
1.观察创建线程状态
```
Thread.State state=thread.getState();
System.out.println(state);//输出new
```
2.观察启动后线程状态
```
thread.start();//启动线程
state=thread.getState();
System.out.println(state);//Run
```
```
3.不断观察线程是否终止
```
while (state !=Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
Thread.sleep(900);
state =thread.getState();//更新线程的状态
System.out.println(state);//输出线程此时的状态
}
```
public class MyThreadStatus {
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("------线程即将结束------");
});
//观察状态
Thread.State state=thread.getState();
System.out.println(state);//输出new
//观察启动后
thread.start();//启动线程
state=thread.getState();
System.out.println(state);//Run
//观察线程是否终止
while (state !=Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
Thread.sleep(900);
state =thread.getState();//更新线程的状态
System.out.println(state);//输出线程此时的状态
}
}
}
结果:
NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
------线程即将结束------
TERMINATED
线程的优先级(Priority)
1.优先级范围是1-10,默认为5,先设置优先级,再执行。
2.设置和查看优先级的方法是:
thread2.setPriority(int);/Thread.currentThread().getPriority()
3.优先级只能加大/减小被调度的概率,执行顺序还是看cup的调度
ublic class MyThreadPriority {
public static void main(String[] args) {
Runnable runnable=()->{
System.out.println(Thread.currentThread().getName()+"的优先级为:"+Thread.currentThread().getPriority());
};
Thread thread1=new Thread(runnable);
Thread thread2=new Thread(runnable);
Thread thread3=new Thread(runnable);
Thread thread4=new Thread(runnable);
Thread thread5=new Thread(runnable);
thread1.start();//使用默认优先级,默认为五,最大为10,最小为1,main线程的优先级也为五
thread2.setPriority(4);
thread2.start();
thread3.setPriority(6);
thread3.start();
thread4.setPriority(6);
thread4.start();
thread5.setPriority(Thread.MAX_PRIORITY);//选择定义好的最大优先级
thread5.start();
}
}
结果:
Thread-3的优先级为:6
Thread-2的优先级为:6
Thread-0的优先级为:5
Thread-4的优先级为:10
Thread-1的优先级为:4
守护线程(daemon)
用户线程:main / gc(垃圾回收),正常都是用户线程,除非
加 Threadd.setDaemon(true):此方法默认为false,为false时代表用户线程
守护线程
1.虚拟机不会等待守护线程执行完毕,只要用户线程执行完毕后,自动结束程序,不管守护线程是否执行完毕
2.创建一个不会主动结束的线程并设置为守护线程,创建一个用户线程,然后启动两线程,一般情况下是守护线程不会停止,也就是程序不会停止,但是,虚拟机是不会管守护线程的,只要用户线程运行完,虚拟机便会结束程序,同时守护线程也停止了。
3.用处:一直监听某个东西,如:监听线程等待时间,如果过长,可结束线程。
//创建一个守护线程;上帝
class God implements Runnable{
@Override
public void run() {
while (true)
System.out.println("上帝一直守护着你!");
}
}
//设置一个用户线程:你自己
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我还活着!");
}
System.out.println("======生命要结束了!======");
}
}
public class MyDaeMon {
public static void main(String[] args) {
//启动守护线程
God god = new God();
Thread thread = new Thread(god);
thread.setDaemon(true);//设置为守护线程的方法,默认为false,为false时表示此线程是用户线程
thread.start();
//启动用户线程
You you = new You();
Thread thread1=new Thread(you);
thread1.start();
}
}
局部结果:
上帝一直守护着你!
上帝一直守护着你!
上帝一直守护着你!
上帝一直守护着你!
我还活着!
我还活着!
我还活着!
我还活着!
======生命要结束了!======
上帝一直守护着你!
上帝一直守护着你!
上帝一直守护着你!
上帝一直守护着你!
上帝一直守护着你!
线程同步机制
需要线程同步的原因:
当多个线程访问同一个对象时,且要修改这个对象的某个东西,如果不加以控制,就会产生,两个线程同时在修改一个对象,或者获取一个东西,但是这个东西如果巧好剩余一个,那么是不可以被两个线程都拿到都,但因为没有加以控制,导致两个线程都拿到了,这种情况是不允许的,所以为了避免这种情况,就需要用到线程同步
线程同步:
当多个线程访问同一个对象并想修改这个对象时,就需要用到线程同步,线程同步是一种等待机制,多个需要访问此对象的线程进入这个对象的等待池,形成队列,等前面用完,下一个线程才能使用,而为了保按顺序使用,就需要用到**锁机制(synchronized)**保证下一个线程只能等前面的线程使用完毕后才能使用,总结就是:队列+锁
锁机制:
当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。但是会产生一些问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换 和 调度延时,引起性能问题。
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
线程不安全示例及原因
1.买票事件:
原因:多个线程抢同一资源,且每一个线程都会获得资源现有数并存储在自己的工作内存,当出现其中一个线程在购买途中被打断且另一购买线程启动并完成购买之后前者线程完成后续购买,那么这两个线程可能会出现重复购买一个资源的现象。
public class Unsafe {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
//设置买票的人
new Thread(buyTicket,"我").start();
new Thread(buyTicket,"你").start();
new Thread(buyTicket,"他").start();
}
}
class BuyTicket implements Runnable{
//定义票
private int ticketNums =10;
private boolean flag =true;//票无时停止信号
@Override
public void run() {
while (flag)
Buy();
}
//判断是否有票,如有,则买一张。
public void Buy(){
if(ticketNums<=0) {
flag=false;
return;
}
//买票
System.out.println(Thread.currentThread().getName()+"买了第"+ticketNums--+"张票");
try {
//让线程停顿一段时间,原因:加大其他线程在票没完时能抢到的几率
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
他买了第10张票
你买了第8张票
我买了第9张票
我买了第7张票
你买了第7张票
他买了第7张票
你买了第6张票
我买了第5张票
他买了第4张票
他买了第3张票
我买了第3张票
你买了第3张票
我买了第2张票
你买了第1张票
他买了第1张票
List集合不安全的原因示例
import java.util.ArrayList;
import java.util.List;
//List集合不安全原因
public class List_Unsafe {
public static void main(String[] args) {
List<String> list=new ArrayList();
//定义多个线程并同时将线程名字录入List集合中
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
结果:
9853
分析:
理想情况是有10000,实际上因为某些线程使用同一个空间,导致某些数据被覆盖,以至于不能达到10000个
同步线程之锁关键字:synchronized
synchronized:作用是:此方法相当于一把锁,每个想调用此方法的对象必须获得这个锁才能调用这个方法,而其他线程只能等前一个线程使用这个方法结束并释放锁后,再次争夺锁的使用权,才能使用此方法。
缺陷:会影响程序执行效率。
synchronized默认锁的是this。
锁的同步块:
synchronized(Obj){}
Obj称之为同步监视器
可以是任何对象,推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者class、
执行步骤:
当有线程访问时,会锁定同步监视器,执行其中代码。其他线程只能等待解锁同步监视器
//因为要使用集合并添加数据,那么list就是要监测的对象所以括号填List集合的对象名list
synchronized (list) {
list.add(Thread.currentThread().getName());
}
例子:
import java.util.ArrayList;
import java.util.List;
//List集合不安全原因
public class List_Unsafe {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList();
//定义多个线程并同时将线程名字录入List集合中
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
//不加延时时,因为线程是同步执行的,可能main方法的线程提前跑完而其他线程还没有跑完所以会出现大小不是10000的情况
Thread.sleep(3000);
System.out.println(list.size());
}
}
结果:
10000
死锁
什么是死锁:两个进程都只拿到了一部分的资源,剩下要的获取的资源都恰巧被对方进程拿着,而且是要进程结束才会释放资源,但是由于双方都无法拿到自己要的资源,所以无法结束,由此现象称为死锁。
锁的显式定义
从jdk5.0开始,通过定义显示同步锁对象来实现同步,同步锁使用Lock对象充当
java.util.concurrent.locks.Lock(接口)是控制多个线程对共享资源进行访问的工具。
ReentrantLock类实现了Lock。
此类的具体代码实现框架:
class A{
private final ReentrantLock lock=new ReentrantLock();
public void m(){
lock.lock();//显式启动锁
try{
//保证线程安全的代码
}
finally{
lock.unlock();//关锁
//如果同步代码有异常,要将unlock()希尔finally语句块
}
}
}
Lock
好处:JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多地子类)
优先使用顺序:
Lock>同步代码块(已经进入方法体,分配了相应资源)>同步方法(在方法体之外)
协作线程(生产者消费者)
wait():
表示线程一直等待,直到其他线程通知,与sleep不同会释放锁
wait(long timeout):指定等待的毫秒数
notify():
唤醒一个处于等待状态的线程
notifyAll()
唤醒同一个东西上所有调用wait()方法的线程,优先级别高的线程优先调度
生产者消费者的协同代码实现
相关概念:
wait()方法是释放锁,notify()方法是获得锁。所以在调用notify()方法时很容易出现:IllegalMonitorStateException异常,
解释:
抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
即:
也就是当前的线程不是此对象监视器的所有者。也就是要在当前线程锁定对象,才能用锁定的对象此行这些方法,需要用到synchronized ,锁定什么对象就用什么对象来执行。
解决方法:
1.用SynChronized代码块监控某个对象
SynChronized (lock) {
lock.notify();
}
2.用关键词synchronized 修饰对应的方法。
public synchronized void A(){
notify();
}
新知:
1.wait()方法和notify()/notifyAll()方法,一般情况下只能在相同类中使用,也建议在相同类中使用,否则很可能出现调用这两种方法后程序却没有出现对应的反应:如:
public class AAA {
public synchronized static void main(String[] args) throws InterruptedException {
BBB bbb=new BBB();
bbb.start();
synchronized (bbb){
bbb.notify();
}
}
}
class BBB extends Thread{
@Override
public synchronized void run() {
try {
System.out.println("线程等待");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程继续");
}
}
结果:
线程等待
上述就是将notify()方法在类外使用,出现的结果明显是不符合理想情况的,究其原因,是因为main方法的线程和子线程不是按顺序执行的,可能出现main方法执行完后子线程还没执行,就会出现先运行了notify()方法,然后再运行子线程并且遇到了wait()方法,以至于线程一直处于等待状态,因为能解锁的notify方法没有了,但是debug方式运行1时却可以按顺序运行出全部的代码,即结果是:
线程等待
线程继续
下面是完整的生产者消费者协同线程运作的代码:
思路:
因为是多线程,所以要创建两个继承Thread的类,分别代表,生产者和消费者线程,还有综合这些资源进行线程协同的类。
第三个类中定义一个资源的变量:ID,无资源时为0,有时为1,定义两个方法:生产者生产方法和消费者方法
两个方法运行过程:先判断有没有资源,即ID是不是为0,如果不为0,则代表资源满了,得让当前线程等待,即调用wait()方法,等待谁呢?等待消费者方法运行且消费资源后,在消费者方法中的最后一行会调用notify()方法,这个方法是用来启动还在等待中的线程(只会启动一个,优先建议使用此方法),那么调用后生产者方法继续运行,即生产物资,且在方法的最后一行再次调用notify()方法启动消费者方法进行物资消费。因为之前物资已经被消费者消费完了,其他的消费者只能等待生产者生产,而这里的消费者数量就是消费者线程中for循环的次数,生产者能生产的物资总和也是生产者线程中的for循环的次数。
由上述可使生产消费协同运作,即完成线程的协同。
public class CT{
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
//消费者线程启动
new Consumer(synContainer).start();
//生产者线程启动
new Productor(synContainer).start();
}
}
//首先需要两条线程:生产者,消费者
//生产者
class Productor extends Thread {
SynContainer synContainer;
Productor(SynContainer synContainer){
this.synContainer=synContainer;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
synContainer.push();
}
}
}
//消费者
class Consumer extends Thread{
SynContainer synContainer;
Consumer(SynContainer synContainer){
this.synContainer=synContainer;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
synContainer.pop();
}
}
}
//其次,需要能操作这两条线程等待以及运行的类和两者争夺的物资
class SynContainer{
//物资状态,开始是无
int ID=0;
int i=0;//生产的数量
int j=0;//消费的数量
//两个方法:生产者放入产品,消费者消费产品
//生产者放入产品
public synchronized void push(){
//如果物资还未被消费,则生产的线程等待
if(ID!=0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果物资被消费了,则生产并物资放入
ID=1;
System.out.println("共生产了"+(++i)+"个物资");
//生产完后启动消费的线程,进行消费
notify();
}
//消费者消费产品
public synchronized void pop(){
if (ID==0){
//代表没有物资进行消费,只能将线程等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有物资可以进行消费,则消费
ID=0;
System.out.println("消费者共消费了"+(++j)+"个物资");
//消费完后启动生产线程,生产
notify();
}
}
结果:
共生产了1个物资
消费者共消费了1个物资
共生产了2个物资
消费者共消费了2个物资
共生产了3个物资
消费者共消费了3个物资
共生产了4个物资
消费者共消费了4个物资
共生产了5个物资
消费者共消费了5个物资
线程池
作用
1.先创建需要用的线程,然后放入线程池,使用时取出来,不使用就重新放入。
2.corePoolSize:核心池大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有让我时最多保持多长时间后会终止
jdk5.0起提供线程池相关API:ExecutorService和Executors
API:ExecutorService:真正的线程池接口,常见子类ThreadPoolExecutor
void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
Futuresubmit(Callable task):执行任务,有返回值,一般用来执行Callable
void shutdown():关闭连接池
Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池
线程池使用代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TextPool {
public static void main(String[] args) {
//创建服务,创建线程池
//newFixedThreadPool 参数为:线程池大小
ExecutorService service= Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
结果:
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4