原视频
https://www.bilibili.com/video/BV1V4411p7EF?spm_id_from=333.999.0.0
一 多任务
一边开车一边打点滴,一边上厕所一边看书,等等。
看似多个任务同时进行,大脑在同一时间只做一件事。
二 多线程
增加公路的道路,减少堵塞。
线程在进程内部,是独立的执行路径。
三 创建线程方法1(继承Thread)
- 在官网下载commons.io包,放入lib文件夹,右键add as library ```java package com.chuan.demo01;
//创建线程:继承Thread //Tip:线程开启未必立即执行,取决于CPU调度 public class TestThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(“hello—“+i); } }
public static void main(String[] args) {
TestThread1 testThread1 = new TestThread1();
testThread1.start();
for (int i = 0; i < 20; i++) {
System.out.println("hi--"+i);
}
}
}
<a name="TMmDg"></a>
## 四 实现多线程同步下载图片
```java
package com.chuan.demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//Thread实现多线程下载图片
public class TestThread2 extends Thread{
private String url;
private String name;
public TestThread2(String url, String name){
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("文件名:"+name);
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://img2.baidu.com/it/u=3150567454,1089677718&fm=253&fmt=auto&app=138&f=PNG?w=600&h=369","1.jpg");
TestThread2 t2 = new TestThread2("https://img1.baidu.com/it/u=470255208,2889809727&fm=253&fmt=auto&app=120&f=JPEG?w=950&h=443","2.jpg");
TestThread2 t3 = new TestThread2("https://img1.baidu.com/it/u=1503222636,2429859130&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500","3.jpg");
t1.start();
t2.start();
t3.start();
}
}
//下载器
class WebDownloader{
public void downloader(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO downloader方法异常");
}
}
}
五 创建线程方法2(实现Runnable)
package com.chuan.demo01;
//创建线程:实现Runnable接口
public class TestThread3 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("hello--"+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 < 20; i++) {
System.out.println("hi--"+i);
}
}
}
⭐小结
继承Thread类
- 子类继承Thread类
- 启动:子类对象.start()
- 不建议使用:避免OOP单继承局限性
继承Runnable接口
- 子类继承Runnable类
- 启动:传入目标对象.Thread对象.start()
- 建议使用:避免单继承局限性,灵活,方便同一对象被多个线程使用
六 实例:火车买票
```java package com.chuan.demo01;
//多个线程操作同一个对象 //火车购票 //问题:多线程操作同资源,线程不安全 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();
}
}
<a name="KCuZI"></a>
## 七 案例:龟兔赛跑
```java
package com.chuan.demo01;
//龟兔赛跑
public class Race implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子
if(Thread.currentThread().getName().equals("兔子")&&i%10==0){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
boolean flag = gameOver(i);
if(flag){
break;
}
System.out.println(Thread.currentThread().getName()+"已跑 "+i+" 步");
}
}
//判断比赛结束
private boolean gameOver(int steps){
if(winner != null){
return true;
}else{
if(steps == 100){
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
八 Callable接口
package com.chuan.demo02;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//线程创建:实现Callable接口
public class TestCallable implements Callable<Boolean> {
private String url;
private String name;
public TestCallable(String url, String name){
this.url = url;
this.name = name;
}
@Override
public Boolean call() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("文件名:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://img2.baidu.com/it/u=3150567454,1089677718&fm=253&fmt=auto&app=138&f=PNG?w=600&h=369","1.jpg");
TestCallable t2 = new TestCallable("https://img1.baidu.com/it/u=470255208,2889809727&fm=253&fmt=auto&app=120&f=JPEG?w=950&h=443","2.jpg");
TestCallable t3 = new TestCallable("https://img1.baidu.com/it/u=1503222636,2429859130&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500","3.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务
ser.shutdownNow();
}
}
//下载器
class WebDownloader{
public void downloader(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO downloader方法异常");
}
}
}
九 静态代理
我帮你做一件事,类似婚庆公司。
package com.chuan.demo03;
//静态代理
public class StaticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new you());
weddingCompany.HappyyMarry();
}
}
interface Marry{
void HappyyMarry();
}
//真实角色
class you implements Marry{
@Override
public void HappyyMarry() {
System.out.println("我们结婚了");
}
}
//代理角色
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
@Override
public void HappyyMarry() {
before();
this.target.HappyyMarry();
after();
}
private void before() {
System.out.println("婚前布置");
}
private void after() {
System.out.println("婚后洞房");
}
}
总结:
- 真实对象和代理对象实现同一个接口
- 代理对象要代理真实对象
- 好处:代理对象可以做真实对象做不了的事
- 真实对象可以专注自己的事
⭐对比解释了方法2的静态代理。
new Thread(() -> System.out.println("love")).start();
new WeddingCompany(new You()).HappyyMarry();
runnable接口中的方法被代理成多线程执行方式
十 Lamba表达式
- 避免匿名内部类定义过多
- 属于函数式编程
- 去掉无意义代码,只留下核心
函数式接口:
- 只包含唯一以恶搞抽象方法的接口
- 可通过lambda表达式创建该接口的对象
package com.chuan.lambda;
//推导lambda表达式
public class TestLambda1 {
//静态内部类
static class DLike implements ILike{
@Override
public void lambda() {
System.out.println("i don't like lambda");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
like = new DLike();
like.lambda();
//局部内部类
like = new ILike(){
@Override
public void lambda() {
System.out.println("i like lambda too");
}
};
like.lambda();
//lambda简化
like = ()-> {
System.out.println("i don't like lambda neither");
};
like.lambda();
}
}
//函数式接口
interface ILike{
void lambda();
}
//实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda");
}
}
带参数式,终极简化
package com.chuan.lambda;
public class TestLambda2 {
public static void main(String[] args) {
ILove love = a-> System.out.println("I love u "+a+" times");
love.love(2);
}
}
interface ILove{
void love(int a);
}
十一 线程停止
线程方法
setPriority(int newPriority) | 更改线程优先级 |
---|---|
static void sleep(long millis) | 在指定毫秒数内瓤当前执行线程休息 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前执行线程对象 |
void interrupt() | 中断线程,不建议使用 |
boolean isAlive() | 测试线程是否处于活动状态 |
停止线程
- 不推荐使用stop(),destroy(),【已废弃】
- 推荐使用标志位进行终止变量,flag = false ```java package com.chuan.state;
public class TestStop implements Runnable{ //设置标志位 private boolean flag = true; @Override public void run() { int i = 0; while(flag){ System.out.println(“Thread:”+i++); } } //公开方法转换标志位 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 < 100; i++) {
System.out.println("Main:"+i);
if(i == 90){
testStop.stop();
}
}
}
}
<a name="WLj5c"></a>
## 十二 线程休眠
- sleep指定当前线程阻塞毫秒数
- sleep存在异常InterruptedException
- sleep时间达到后,线程进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每个对象有一个锁,sleep不会释放锁
```java
package com.chuan.state;
//模拟倒计时
public class TestSleep2 {
public static void main(String[] args) {
try {
tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void tenDown() throws InterruptedException {
int num = 10;
while(true){
Thread.sleep(1000);
System.out.println(num--);
if(num<=0){
break;
}
}
}
}
十三 线程礼让
- 让CPU重新调度,礼让未必成功,取决于CPU ```java package com.chuan.state;
//测试礼让线程 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()+”停止”); } }
<a name="M3Scp"></a>
## 十四 线程强制执行
- JOIN合并线程,阻塞其他线程先执行此线程
- 可理解成插队
```java
package com.chuan.state;
//测试Joi方法
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程vip:"+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 100; i++) {
if(i==20){
thread.join();
}
System.out.println("main:"+i);
}
}
}
十五 线程状态观测
package com.chuan.state;
//观察测试线程状态
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("/////");
});
//观察状态
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(100);
state = thread.getState();
System.out.println(state);
}
}
}
十六 线程的优先级
package com.chuan.state;
//测试线程优先级
public class TestPriority{
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
Thread t5 = new Thread(myPriority);
Thread t6 = new Thread(myPriority);
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
// t5.setPriority(-1);
// t5.start();
// t6.setPriority(11);
// t6.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
因为CPU性能问题,并没有按照预期的结果显示。额外添加代码,通过sleep()模拟网络延迟。
十七 守护线程
- 线程分用户线程和守护线程
- 虚拟机须确保用户线程执行完毕,不用等待守护线程执行完毕
- 如:后台记录操作日志、内存监控、垃圾回收等 ```java package com.chuan.state;
//上帝守护你 public class TestDaemon { public static void main(String[] args) { God god = new God(); You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);
thread.start();//上帝守护线程启动
new Thread(you).start();//你用户线程启动
}
}
//God class God implements Runnable{ @Override public void run() { while(true){ System.out.println(“God bless u”); } } }
//You class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println(“live happily”); } System.out.println(“=====bye~bye=====”); } }
<a name="HvIoa"></a>
## 十八 线程同步
形成条件:队列+锁
锁机制 synchronized,存在问题
- 线程持有锁会将其他相关线程挂起
- 多线程加锁释放锁会导致上下文切换,调度延时,引发性能问题
- 高优先级线程等待低优先级线程释放锁,会导致倒置
<a name="gXpYT"></a>
## 十九 三大不安全案例
- 不安全买票
- 不安全取钱
- 不安全线程
```java
package com.chuan.syn;
//不安全买票
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station,"小明").start();
new Thread(station,"小红").start();
new Thread(station,"黄牛").start();
}
}
class BuyTicket implements Runnable{
//票
private int ticketNums = 10;
//外部停止
boolean flag = true;
@Override
public void run() {
//买票
while(flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if(ticketNums<=0){
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"获取"+ticketNums--);
}
}
结果出现 -1
package com.chuan.syn;
//不安全取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"梦想基金");
Drawing you = new Drawing(account,50,"你");
Drawing me = new Drawing(account,100,"我");
you.start();
me.start();
}
}
//账户
class Account{
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread{
Account account;
int drawingMoney;
int nowMoney;
public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"没钱了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - drawingMoney;
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name+"余额:"+account.money);
System.out.println(this.getName()+"手头:"+nowMoney);
}
}
package com.chuan.syn;
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
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(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
二十 同步方法及同步块
同步方法
同步块:synchronized(Obj){}
Obj称为同步监视器,可以是任何对象,推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器,默认是对象本身
- 不安全买票
将buy()方法上锁,发现票都被小明取完了。修改代码,在run的内部循环模拟延时。成功=>
- 不安全取钱
锁的对象是变化的量成功=>
- 不安全线程
二十一 CopyOnWriteArrayList
JUC
package com.chuan.syn;
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型集合
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
二十二 死锁
package com.chuan.Thread;
//死锁
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"灰姑娘");
Makeup g2 = new Makeup(1,"白雪公主");
g1.start();
g2.start();
}
}
class Lipstick{
}
class Mirror{
}
class Makeup extends Thread{
//static表示只有一份资源
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//化妆,互相持有对方的锁
private void makeup() throws InterruptedException {
if(choice == 0){
synchronized (lipstick){
System.out.println(this.girlName+"获得口红锁");
Thread.sleep(1000);
synchronized (mirror){
System.out.println(this.girlName+"获得镜子锁");
}
}
}else{
synchronized (mirror){
System.out.println(this.girlName+"获得镜子锁");
Thread.sleep(2000);
synchronized (lipstick){
System.out.println(this.girlName+"获得口红锁");
}
}
}
}
}
修改代码位置,破坏了请求与保持条件。
产生死锁四个必要条件
- 互斥条件:
- 请求与保持条件
- 不剥夺条件
- 循环等待条件
二十三 Lock(锁)
JDK5.0开始,可通过显示定义Lock对象实现同步。
java.util.concurrent.locks.Lock接口控制多个线程对共享资源进行访问。锁提供对共享资源独占访问,每次只能由一个线程对Lock对象加锁。
ReentrantLock类实现Lock。
package com.chuan.senior;
import java.util.concurrent.locks.ReentrantLock;
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 tickertNums = 10;
//定义lock锁
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try {
lock.lock();
if(tickertNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tickertNums--);
}else{
break;
}
}finally {
lock.unlock();
}
}
}
}
对比synchronized与lock
- Lock是显示锁(手动开关),synchronized是隐式锁,出作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM花费较少时间调度线程,性能更好,具有更好扩展性(更多子类)
-
二十四 生产者消费者问题
线程同步问题,生产者和消费者共享同一资源,且两者间相互依赖,互为条件。
生产者在没生产产品前,通知消费者等待,生产了后又需要通知消费者消费
- 消费者在消费后,要通知生产者生产新的产品
- 在生产消费者问题中,仅有synchronized是不够的
- synchronized可阻止并发更新同一共享资源,实现同步
- synchronized不能实现不同线程间消息传递
wait() wait(long timeout) notify() notifyAll()
解决方式一:管程法
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
解决方式二:信号灯法
判断标志位
二十五 管程法
package com.chuan.senior;
//测试生产者消费者模型
//方式一:管程法
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
//生产者
class Producer extends Thread{
SynContainer container;
public Producer(SynContainer container){
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了"+i+"只鸡");
container.pop();
}
}
}
//鸡
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
Chicken[] chickens = new Chicken[10];
int count = 0;
public synchronized void push(Chicken chicken){
if(count == chickens.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count++] = chicken;
//通知消费者消费
this.notifyAll();
}
public synchronized Chicken pop(){
if(count == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken = chickens[count];
//通知生产者生产
this.notifyAll();
return chicken;
}
}
二十六 信号灯法
package com.chuan.senior;
//测试生产者消费者模型
//方式一:信号灯法
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 < 20; 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 < 20; i++) {
this.tv.watch();
}
}
}
class TV{
String voice;
boolean flag = true;
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 = !this.flag;
}
public synchronized void watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了:"+voice);
this.notifyAll();
this.flag = !this.flag;
}
}
二十七 线程池
- JDK5.0起提供线程池相关API:ExecutorService和Executors
- ExecutorService:真正线程池接口,常见子类ThreadPoolExecutor
- void execute(Runnable command):执行任务,无返回值,一般执行Runnable
Future submit(Callable task):执行任务,有返回值,一般执行Callable - void shutdown():关闭线程池
- Executors:工具类 ```java package com.chuan.senior;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
public class TestPool { public static void main(String[] args) { 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()); } } ```