多线程详解
线程简介
- 程序、进程、线程的关系
- 什么是线程:线程是程序中执行的线程 Java虚拟机允许程序同时运行多个执行线程。
- 进程:说起进程,就不得不说程序,程序是指令和数据得有序集合,其本身没有任何运行的意义,是一个静态的概念;而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。
- 通常在一个进程中乐意包含若干个线程,当然一盒进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位、
- 线程就是独立执行路径。
- main()称之为主线程,为系统的入口,用于执行整个程序
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系系统紧密相关的,先后顺序不能人为干预。
- 对于同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
- 线程会带来额外的开销,如cpu调度时间,并发控制开销。
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
线程实现(重点)
1、继承Thread类
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
- 不建议使用 避免OOP单继承局限性
//注意: 线程开启不一定立即执行 由cpu调度执行
public class ThreadDemo1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("线程for"+i);
}
}
}
class Test{
public static void main(String[] args) {
//创建线程对象
ThreadDemo1 threadDemo1 = new ThreadDemo1();
//调用start方法开启线程
threadDemo1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("循环for"+i);
}
}
}
//网图下载 记得导入io-jar
package com.zhhl.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class ThreadDownLoad extends Thread {
private String url; //图片的url地址
private String name; //文件的名字
public ThreadDownLoad(String url,String name){
this.url=url;
this.name=name;
}
//下载线程图片的执行体
@Override
public void run() {
WebDownload webDownload = new WebDownload();
webDownload.downLoader(url,name);
System.out.println("打印文件的名字为:"+name);
}
//主线程函数
public static void main(String[] args) {
ThreadDownLoad t1 = new ThreadDownLoad("http://img.netbian.com/file/2020/0828/small24f6fab40f7cb88c6ab720e5182bbe2f1598629198.jpg","01.jpg");
ThreadDownLoad t2 = new ThreadDownLoad("http://img.netbian.com/file/2020/0820/smalla8c44abd984a8665ecec6a33cf709ec11597929475.jpg","02.jpg");
ThreadDownLoad t3 = new ThreadDownLoad("http://img.netbian.com/file/2020/0818/small4769624e404775ab1f8e0ccdacb736711597684658.jpg","03.jpg");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
//下载器
class WebDownload{
//下载方法
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异常");
}
}
}
2、实现Runnable接口
- 定义Runnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()启动线程
- 推荐使用,避免了单继承局限性,灵活方便,方便同一个对象被多个线程使用
public class RunnableDemo implements Runnable {
//线程执行体
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("线程======="+i);
}
}
public static void main(String[] args) {
//创建Runable接口的实现类对象
RunnableDemo runnableDemo = new RunnableDemo();
//创建线程对象 对象线程对象来开启我们的线程
// Thread thread = new Thread(runnableDemo);
// thread.start();
new Thread(runnableDemo).start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程---"+i);
}
}
}
//图片下载
package com.zhhl.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class RunnableDownLoad implements Runnable {
private String url;
private String name;
public RunnableDownLoad(String url,String name){
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downLoad(url,name);
System.out.println("下载文件的名字:"+name);
}
//主函数
public static void main(String[] args) {
//创建实现类对象
RunnableDownLoad r1 = new RunnableDownLoad("http://img.netbian.com/file/2020/0828/small24f6fab40f7cb88c6ab720e5182bbe2f1598629198.jpg","01.jpg");
RunnableDownLoad r2= new RunnableDownLoad("http://img.netbian.com/file/2020/0820/smalla8c44abd984a8665ecec6a33cf709ec11597929475.jpg","02.jpg");
RunnableDownLoad r3 = new RunnableDownLoad("http://img.netbian.com/file/2020/0818/small4769624e404775ab1f8e0ccdacb736711597684658.jpg","03.jpg");
//创建线程对象并启动线程
new Thread(r1).start();
new Thread(r2).start();
new Thread(r3).start();
}
}
//下载器
class WebDownLoader{
public void downLoad(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
System.out.println("IO异常");
}
}
}
3、实现Callable接口
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务 ExecutorService e= Executors.newFixedThreadPool(3);
- 提交执行 Future s1 = e.submit(r1);
- 获取结果 boolean rs1 = s1.get();
- 关闭服务 e.shutdownNow();
package com.zhhl.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class CallableDemo implements Callable<Boolean> {
private String url;
private String name;
public CallableDemo(String url,String name){
this.url = url;
this.name = name;
}
@Override
public Boolean call() {
WebDownLoad webDownLoad = new WebDownLoad();
webDownLoad.downLoad(url,name);
System.out.println("下载文件的名字:"+name);
return true;
}
//主函数
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建实现类对象
CallableDemo r1 = new CallableDemo("http://img.netbian.com/file/2020/0828/small24f6fab40f7cb88c6ab720e5182bbe2f1598629198.jpg","01.jpg");
CallableDemo r2= new CallableDemo("http://img.netbian.com/file/2020/0820/smalla8c44abd984a8665ecec6a33cf709ec11597929475.jpg","02.jpg");
CallableDemo r3 = new CallableDemo("http://img.netbian.com/file/2020/0818/small4769624e404775ab1f8e0ccdacb736711597684658.jpg","03.jpg");
//创建执行服务
ExecutorService e= Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> s1 = e.submit(r1);
Future<Boolean> s2= e.submit(r2);
Future<Boolean> s3 = e.submit(r3);
//获取结果
boolean rs1 = s1.get();
boolean rs2 = s2.get();
boolean rs3 = s3.get();
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
//关闭服务
e.shutdownNow();
}
}
//下载器
class WebDownLoad{
public void downLoad(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
System.out.println("IO异常");
}
}
}
4、初识并发问题
//多个线程操作同一份资源的情况下,线程不安全,数据紊乱
public class ConcurrentDemo implements Runnable {
private int ticket_num=10;
@Override
public void run() {
while (true){
if (ticket_num<=0){
break;
}
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket_num--+"张票");
}
}
public static void main(String[] args) {
ConcurrentDemo con = new ConcurrentDemo();
new Thread(con,"小明").start();
new Thread(con,"小红").start();
new Thread(con,"黄牛党").start();
}
}
//龟兔赛跑案例
public class Race implements Runnable {
//胜利者
public 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(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOvers(i);
//如果比赛结束就停止程序
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
}
}
//判断比赛是否结束
public boolean gameOvers(int stemp){
//判断是否有胜利者
if(winner!=null){
return true;
}else {
if (stemp>=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();
}
}
5、静态代理
package com.zhhl.thread;
//真实对象和带理对象都要实现同一个接口
//代理对象要代理真实角色
//代理对象可以做真实对象做不了的事情
//真实对象专注于自己的事情
public class StaticPorxy {
public static void main(String[] args) {
// WeddingCompany weddingCompany = new WeddingCompany(new You());//你要结婚 传you对象 YOU you = new You();
// weddingCompany.happrMarry();
new Thread(()-> System.out.println("我爱你")).start(); //对比 happrMayyr()相当于线程里的start()
new WeddingCompany(new You()).happrMarry(); //上面两句的整合
}
}
interface Marry{
void happrMarry();
}
//真实角色
class You implements Marry{
@Override
public void happrMarry() {
System.out.println("我要结婚了 好开心");
}
}
//代理角色
class WeddingCompany implements Marry{
//代理真实目标角色
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void happrMarry() {
before();
this.target.happrMarry();//真实对象
after();
}
private void after() {
System.out.println("结婚之后,收缴尾款");
}
public void before(){
System.out.println("结婚之前,布置现场");
}
}
6、lamda
package com.zhhl.thread;
public class Lamda {
//静态内部类
/* static class Lover implements Love{
@Override
public void love(int a) {
System.out.println("i love you"+a);
}
}*/
public static void main(String[] args) {
//局部内部类
/* class Lover implements Love{
@Override
public void love(int a) {
System.out.println("i love you"+a);
}
}*/
// Love love = new Lover();
// love.love(2);
//匿名内部类 通过接口实现
Love love =new Love() {
@Override
public void love(int a) {
System.out.println("i love you"+a);
}
};
love.love(2);
}
}
//定义接口
interface Love{
void love(int a);
}
//外部实现类
/*
class Lover implements Love{
@Override
public void love(int a) {
System.out.println("i love you"+a);
}
}*/
//=====================================================================
//lamda简化
package com.zhhl.thread;
public class Lmada2 {
public static void main(String[] args) {
//lmada表达式简化
Love love = (int a)->{
System.out.println("Iloveyou"+a);
};
love.love(520);
//简化参数类型 多参数也可简化 用逗号隔开
love = (a)->{
System.out.println("Iloveyou2"+a);
};
love.love(521);
//简化括号 多个参数则不行
love = a->{
System.out.println("Iloveyou3"+a);
};
love.love(522);
//简化花括号 只有一行代码才可以简化 接口为函数式接口(接口只有一个方法)
love = a-> System.out.println("Iloveyou4"+a);
love.love(523);
}
}
线程状态
package com.zhhl.thread;
//建议线程正常停止--->利用次数 不建议死循环
//建议使用标识位 设置一个标识位
//不要使用stop后者destory过时的以及JDK不建议使用的方法
public class ThreadStop implements Runnable {
//设置一个标识位
private boolean flag = true;
@Override
public void run() {
int i=0;
while (flag){
System.out.println("run ===thread"+i++);
}
}
//设置一个公开的方法停止线程 转换标识位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
ThreadStop threadStop = new ThreadStop();
new Thread(threadStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if (i==900){
threadStop.stop();
System.out.println("线程该停止了");
}
}
}
}
线程方法
- setPriority(int newPriority) 更改线程的优先级
- static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
- void join() 等待该线程终止
- static void yield() 暂停当前正在执行的线程对象,并执行其他线程
- void interrupt() 中断线程 别用这个方式
- boolean isAlive() 测试是否处于活动状态
线程休眠
- sleep(时间) 指定当前线程阻塞的毫秒数
- sleep存在InterruptedException;
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延时
- 每一个对象都有一个锁,sleep不会释放锁
//模拟倒计时
public class ThreadSleep {
public static void main(String[] args) {
testTime();
}
public static void testTime(){
int i = 10;
while (true){
try {
Thread.sleep(1000); //每一秒打印一次
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i--);
if (i<0){ break;
}
}
}
}
//打印当前时间
//打印当前时间
public static void main(String[] args) {
Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("YY:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程礼让
- 礼让线程,让当前正在执行的线程暂停 ,但不阻塞,将线程从运行状态转为就绪状态
- 让cpu重新调度,礼让有时候不会成功
package com.zhhl.thread;
public class ThreadYield {
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()+"线程执行");
}
}
合并线程
- join合并线程,待此线程执行完后,再执行其他线程
- 可以想象成插队
package com.zhhl.thread;
public class ThreadJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("线程vip来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
ThreadJoin threadJoin = new ThreadJoin();
Thread thread = new Thread(threadJoin);
for (int i = 0; i < 500; i++) {
if (i==100){
thread.start();
thread.join();//插队
}
System.out.println("主线程"+i);
}
}
}
线程状态
- NEW 尚未启动的线程处于此状态
- RUNNABLE 在java虚拟机中执行的线程处于此状态
- BLOCKED 被阻塞等待监视器锁定的线程处于此状态
- WAITING 正在等待另一个线程执行特定动作的线程处于此状态
- TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
- TERMINATED 已退出的线程处于此状态
package com.zhhl.thread;
public class ThreadState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(500);
} 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);
thread.getState();//更新线程状态
System.out.println(state);
}
}
}
守护线程
- 线程分为用户线程合守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等
package com.zhhl.thread;
//守护线程
public class ThreadDeamo {
public static void main(String[] args) {
Yous yous = new Yous();
God god = new God();
Thread thread = new Thread(god);
thread.setDaemon(true); //默认为false 表示是用户线程 正常的线程都是用户线程
thread.start(); //god守护线程
new Thread(yous).start(); //你 用户线程停止
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝守护着你");
}
}
}
//你
class Yous implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("用心活好每一天");
}
System.out.println("++byebye++============================");
}
}
线程同步
synchronized关键字
- 可分为同步方法跟同步块
- 同步方法:public synchronized void buy() {}
- 同步块:sunchronized(对象){} 默认锁的是this 正确应该锁执行增删改查的对象 也就是锁的变化的量
//案例
//方法锁
package com.zhhl.thread;
//线程不安全 synchronized可使变为安全
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();
//模拟延时
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void buy() {
//判断是否有票
if (ticketNums<=0){
flag = false;
return;
}
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
//案例二
//同步块
package com.zhhl.thread;
//不安全取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"结婚基金");
Drawing you = new Drawing(account,50,"你");
Drawing youGirl = new Drawing(account,100,"youGirl");
you.start();
youGirl.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() {
synchronized (account){ //同步块
//判断没有前
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);
//Thread.currentThread().getName() = this.getName();
System.out.println(this.getName()+"手里的钱"+nowMoney);
}
}
}
JUC安全集合测试
package com.zhhl.thread;
import java.util.concurrent.CopyOnWriteArrayList;
//JU安全类型集合测试
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
copyOnWriteArrayList.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(copyOnWriteArrayList.size());
}
}
死锁
产生死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求在资源而阻塞时,对以获得的资源保持不放
- 不剥夺条件:进程以获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之前形成首尾相接的循环等待资源关系
//死锁案例
package com.zhhl.thread;
public class DeadLock {
public static void main(String[] args) {
Makeup makeup = new Makeup(0,"灰姑娘");
Makeup makeup2 = new Makeup(1,"白雪公主");
makeup.start();
makeup2.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锁
lock是显示锁 手动开启和关闭 ,synchronized是隐式锁,出了作用域自动释放
lock只有代码块锁
使用lock锁,JVM将花费较少的时间来调度线程,性能更好,并且有良好的可扩展性
优先使用顺序:Lock >同步代码块(已经进入了方法体 ,分配了相应资源)> 同步方法(在方法体之外)
定义可重入锁:
private final ReentrantLock reentrantLock = new ReentrantLock();
package com.zhhl.thread;
import java.util.concurrent.locks.ReentrantLock;
public class Lock {
public static void main(String[] args) {
TestLock testLock = new TestLock();
new Thread(testLock,"小明").start();
new Thread(testLock,"小红").start();
new Thread(testLock,"小王").start();
}
}
class TestLock implements Runnable{
int ticketNums = 10;
//定义可重入锁
private final ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
reentrantLock.lock();//加锁
if (ticketNums>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ticketNums--);
}else {
break;
}
}finally {
reentrantLock.unlock();//解锁
}
}
}
}
线程通信问题
信号灯法
package com.zhhl.thread;
//信号灯法
public class TestPc {
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++) {
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;
}
}
线程池
package com.zhhl.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.*;
public class TestPool {
public static void main(String[] args) {
//创建服务 创建线程池
//参数为线程池大小
ExecutorService es = Executors.newFixedThreadPool(10);
//执行
es.execute(new MyThread());
es.execute(new MyThread());
es.execute(new MyThread());
es.execute(new MyThread());
//关闭连接
es.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}