java高并发基础概念
什么是进程?
进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位。每一个进程都有一个自己的地址空 间,即进程空间或(虚空间)。进程空间的大小 只与处理机的位数有关,一个 16 位长处理机的进程空间大小为 216 ,而 32 位处理机的进程空间大小为 232 。进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。进程拥有一个完整的虚拟地址空间,不依赖于线程而独立存在
什么是程序?
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念;进程是执行程序的一次执行过程,它是一个动态的概念,是资源分配的单位,一个进程中可以包含有若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义,线程是CPU调度和执行的单位
什么是线程?
线程,在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创建一个进程显然是行不通的,——无论是从系统资源开销方面或是响应用户请求的效率方面来看。因此,操作系统中线程的概念便被引进了。线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是 CPU 调度的一个基本单位。线程是进程的一部分,没有自己的地址空间,与进程内的其他线程一起共享分配给该进程的所有资源。
什么是协程?
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
启动一个java程序
名称 | 作用 |
---|---|
jps | 显示系统内所有Hotspot虚拟机进程 |
jstat | 用于收集Hotspot虚拟机各个方面的运行数据 |
jinfo | 显示虚拟机配置信息 |
jmap | 生成虚拟机的内存转储快照 |
jstack | 生成虚拟机的线程快照 |
jhat | 用于分析heapdump文件,建立http/html服务器,使得用户在浏览器中访问 |
jps [options] [hostid]
参数有:
-p 只输出LVMID,省略主类的名称
-m 输出虚拟机进程启动时传递给主类main的函数参数
-l 输出主类的全名,如果进程执行的是jar包,输出jar路径
-v 输出虚拟机进程启动时jvm参数
java多线程与并发编程
1.多线程的三大特性
原子性: java的内存模型保证了基本数据类型的操作具备原子性的,对于double和long类型的数据非原子操作,虚拟机提供了字节码指令monitorenter和moniterexit进行lock和unlock
可见性:可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。java内存模型是通过将工作内存中的变量值同步到主内存中,依赖于主内存作为媒介的方式实现可见性。Volatile关键字保证了线程之间的可见性。java中还能够保证可见性的有cynchronized(是同步快对一个变量unlock时,必须先把此变量同步到主内存中)和final关键字(在构造器中完成初始化后,构造器没有将this的引用传递出去,对于其他线程就可见final的值)
有序性:java内,一个线程内部所有的操作都是有序的,如果在一个线程中观察另一个线程,所有的操作都是无序的。java中使用volatile和synchronized两个关键字来保证线程之间操作的有序性,Synochronized是一个变量在同一时刻只允许一条线程对其进行lock操作。
2.java内存模型
可以参考另一篇文章:
3.Volatile关键字
volatile与synchronized的区别:
a.volatile轻量级,只能修饰变量,synchronized重量级,还可以修饰方法
b.volatile只能保证数据的可见性,不能够用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞
c.synchronized不仅可以保证可见性,还保证原子性,因为只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句全部执行,多个线程之间争抢synchronized锁对象时,会出现阻塞。
4.ThrreadLocal
提高一个线程的局部变量,访问某个线程拥有自己的局部变量
当使用threadlocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响到其他线程对应的副本。
ThreadLocal通过map集合,Map.put(“当前线程”,值);
5.线程池
什么是线程池?
线程池是指在初始化一个多线程应用程序的过程中创建一个线程集合,然后需要执行新的任务时重用这些线程而不是新建一个线程。线程池的个数完全取决于可用内存数量和应用程序的需求。
线程池的作用:
1.线程池改进了一个应用程序的响应时间,由于线程池中的线程已经准备好等待被分配任务,应用程序可以直接拿了使用而不用新建一个线程
2.线程为每个短生存周期任务创建一个完整的开销并可以在任务完成后回收资源
3.线程池根据当前在系统中运行的进程来进行优化线程时间片
4.线程池允许我们开启多个任务而不用为每个线程设置属性
5.线程池允许我们正在执行的任务的程序参数传递一个包含状态信息的对象
6.线程池可以用来解决处理一个特定的求情最大线程数量限制问题
线程池的四种创建方式:
Executors提供的四种线程池
1.newCachedThreadPool:创建一个可以缓存的线程池,如果线程池长度超过处理需要,可灵活回收空余线程
2.newFixedThreadPool:创建一个定长的线程池,可控制线程最大并发数,超出线程会在队列中等待
3.newScheduledThreadPool:创建一个定长的线程池,支持定时及周期任务执行
4.newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定的顺序执行。
线程池使用时会用到的概念:
核心数——》任务队列——》最大线程数——》拒绝策略
个人理解该过程类似于银行业务,一开始有三个业务窗口,两个VIP窗口,顾客少的时候不用排队,再多的人时,安排叫号,使其在候选区域等候,当再多人员时,开放vip窗口办理业务,当人数过多时,选择一定的拒绝策略,让多余的人不用等候。
拒绝策略有四种:
1.AbortPolicy:抛出异常,RejectedExecutionException
2.DisCardPolicy:什么也不做,直接忽略
3.DiscardOldestPolicy:丢弃执行队列中最老的任务,尝试为当前提交的任务腾出位置
4.CallerRunsPolicy:直接由提交任务者执行这个任务
线程以及线程实例
1.线程的创建(三种)
继承Thread类、实现Runnable接口、实现Callable接口
1.1继承Thread类
重写run方法,使用线程从网上下载一个图片
public class TestDemo02 extends Thread{
private String url;
private String name;
@Override
public void run() {
WebDownloader ed = new WebDownloader();
ed.downloader(url,name);
System.out.println("下载文件名为"+name);
}
public TestDemo02(String url,String name){
this.url = url;
this.name = name;
}
public static void main(String[] args) {
TestDemo02 t1 = new TestDemo02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595563900064&di=efff05e7793f5b061865ceebb724c354&imgtype=0&src=http%3A%2F%2Fpic.feizl.com%2Fupload%2Fallimg%2F170614%2F0913162K0-3.jpg","./file1.jpg");
TestDemo02 t1 = new TestDemo02("fileurl","filename.txt");
t1.start();
t2.start();
}
}
//下载器,使用工具类commons-io.jar
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异常");
}
}
}
1.2实现runnable接口
public class TestDemo03 implements Runnable{
public static void main(String[] args) {
//创建实现Runnable接口的对象t1
TestDemo03 t1 = new TestDemo03();
//调用方式
new Thread(t1).start();
}
@Override
public void run() {
System.out.println("runnable");
}
}
Thread类:
子类继承Thread类
子类对象.start()方法
继承Thread类后不能再继承其他的类实现Runnable接口,实现重写run方法
启动线程的方式:传入目标对象+Thread对象.start()
避免了单继承的局限性,灵活方便,方便同一个对象被多个线程使用
并发带来的问题
并发问题发生在多个线程对同一个共享资源或静态资源操作的时候,例如一个购票场景,可能或有多余的票,存在,一下是一个存在线程安全的程序。
public class TestDemo04 implements Runnable {
private int ticket = 10;
@Override
public void run() {
while(true){
if(ticket<=0){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"获取到第"+ticket--+"票");
}
}
public static void main(String[] args) {
TestDemo04 t1 = new TestDemo04();
new Thread(t1,"gizz").start();
new Thread(t1,"guy").start();
new Thread(t1,"heihei").start();
}
}
视频中龟兔赛跑的小例子,兔子设计的速度比乌龟快,但是兔子被胜利冲昏了头脑,睡了一觉,赢得或许就不是自己了
public class Race implements Runnable {
private static String winner;//标识唯一的胜利者
//每个人跑到100,谁赢
@Override
public void run() {
for(int i=0;i<=100;){
if(Thread.currentThread().getName().equals("兔子")&&i%99==0){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(hasWin(i))break;
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
if(Thread.currentThread().getName().equals("兔子"))i+=2;
else i++;
}
}
//判断是否已经存在胜利者了
public boolean hasWin(int step){
if(winner!=null) return true;
if(step==100){
winner = Thread.currentThread().getName();
System.out.println("winner is"+winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race r=new Race();
new Thread(r,"兔子").start();
new Thread(r,"乌龟").start();
}
}
1.3实现Callable接口
1.实现Callable接口,需要指定返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(n);
5.提交执行: Future
result = ser.submit(T); 6.获取结果: boolean r1 = result.get();
7.关闭服务:ser.shutdownNow();
public class TestDemo05 {
public static void main(String[] args) {
//使用FutureTask类创建线程对象并获取返回值
FutureTask<Integer> futureTask = new FutureTask(new CallDemo());
new Thread(futureTask).start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//实现Callable接口,重写call方法,含有返回值
class CallDemo implements Callable<Integer>{
private int sum = 0;
@Override
public Integer call() throws Exception {
for (int i = 0; i < 100; i++) {
sum++;
}
return sum;
}
}
2.静态代理
被代理对象与代理兑现实现同一个接口,代理对象调用被代理对象的真正的接口方法,被代理对象专注做自己的事情,代理对象做更多其他的事情。
/**
* @author xing
* @create 2020/7/24-concurrent
*/
public class StaticProxyDemo {
public static void main(String[] args) {
WedingCompany wdc = new WedingCompany(new Man(),new WeMan());
wdc.happyMarry();
}
}
//接口
interface Marry{
void happyMarry();
}
//被代理的对象1
class Man implements Marry{
@Override
public void happyMarry() {
System.out.println("新郎很开心");
}
}
//被代理的对象2
class WeMan implements Marry{
@Override
public void happyMarry() {
System.out.println("新娘很开心");
}
}
//代理对象,做其他的事情,接口中的具体事情调用被代理对象的方法
class WedingCompany implements Marry{
private Man m ;
private WeMan wm;
public WedingCompany(Man m ,WeMan wm){
this.m = m;
this.wm = wm;
}
@Override
public void happyMarry() {
before();
m.happyMarry();
wm.happyMarry();
after();
}
public void before(){
System.out.println("布置婚礼现场");
}
public void after(){
System.out.println("收钱,收拾结婚现场");
}
}
3.Lambda表达式
$$
\lambda 是希腊字母中排序第十一位的字母,读作Lambda
$$
函数式编程:
(params)->expression[表达式]
(params)->statement[语句]
(params)->{statements}
关键所在是需要有一个函数式接口 只有一个抽象方法的接口
定义一个接口,定义一个实现类实现接口以及实现接口的方法,由于该类的对象可能只使用一次,因此使用一个静态内部类以及局部内部类简化,对于接口也可以使用匿名内部类简化,java8使用lambda对函数式接口做了更多简化。
public class LambdaDemo {
//3.静态内部类
static class Like2 implements Ilike{
@Override
public void lambda() {
System.out.println("静态内部类");
}
}
public static void main(String[] args) {
Ilike like = new Like();
like.lambda();
like = new Like2();
like.lambda();
//4.局部内部类
class Like3 implements Ilike{
@Override
public void lambda() {
System.out.println("局部内部类");
}
}
like = new Like3();
like.lambda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
like = new Ilike() {
@Override
public void lambda() {
System.out.println("匿名内部类");
}
};
like.lambda();
//6.lambda简化
like = ()-> System.out.println("lambda");
like.lambda();
}
}
//1.创建接口对象
interface Ilike{
void lambda();
}
//2.创建实现类
class Like implements Ilike{
@Override
public void lambda() {
System.out.println("i like lambda");
}
}
线程的状态
- new Thread(); 新建状态
- start(); 就绪状态
- run(); 执行状态
- 中断或者结束时 死亡状态
- sleep()等方法 阻塞状态
方法 | 说明 |
---|---|
setPripority(int newPripority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前正在执行的现场呢过对象,并执行其他线程 |
void interrupt() | 中断线程,一般不使用 |
boolean isAlive() | 测试现场呢过是否存活 |
线程停止
jdk提供的stop、destory方法已经废弃不建议使用,一般希望线程自己停下来,自己使用一个标志位进行终止变量使得线程停止。
线程休眠
1.sleep(时间) 指定当前线程阻塞的毫秒数
2.sleep存在异常InterruptedException
3.sleep时间达到后线程进入就绪状态
4.sleep可以模拟网络延迟,倒计时等
5.每个对象都有一个锁,sleep不会释放锁
使用sleep(时间)模拟网络延迟,放大问题的发生性。
使用sleep倒计时,每秒钟递减一
public static void testDown() throws InterruptedException {
int num = 10;
while(true){
Thread.sleep(1000);
System.out.println(num--);
if(num<=0){
break;
}
}
}
线程礼让 yield
礼让线程,让当前正在执行的现场线程暂停,但不阻塞
将线程从运行状态转为就绪状态
礼让是让cpu重新调度,但不一定成功,
public class YieldDemo {
public static void main(String[] args) {
MyYield yield = new MyYield();
new Thread(yield,"a").start();
new Thread(yield,"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方法
public class JoinDemo 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 {
JoinDemo jd = new JoinDemo();
Thread thread = new Thread(jd);
thread.start();
//主函数执行,会被插队
for (int i = 0; i <100 ; i++) {
if(i==20){
thread.join();
}
System.out.println("main"+i);
}
}
}
线程的优先级
java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程去执行
线程优先级用数字表示,范围从1~10
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
使用以下方式改变或获取优先级
getPriority();setPriority(int xxx)
public class PriorityDemo {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority th1 = new MyPriority();
Thread th3= new Thread(th1,"低优先级");
th3.setPriority(4);
th3.start();
Thread th2 = new Thread(th1,"高优先级");
th2.setPriority(8);
th2.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"在执行");
}
}
守护(daemon)线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
后台记录操作日志、监控内存、垃圾回收等待
public class DaemonDemo {
public static void main(String[] args) {
God god= new God();
People people = new People();
Thread goddeame = new Thread(god);
//设置线程为守护线程
goddeame.setDaemon(true);
goddeame.start();
new Thread(people).start();
}
}
//守护现场
class God implements Runnable{
@Override
public void run() {
while(1==1){
System.out.println("守护你");
}
}
}
//一般线程
class People implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("活着不易");
}
System.out.println("=========GoodBye World!===========");
}
}
线程同步
同一个对象(全局变量或静态变量)被多个线程同时操作,线程同步问题的解决使用的是队列+锁 (Synchronized)
同步快Synchronized(Obj){},Obj称之为同步监视器,一般使用共享的资源作为同步监视器。
同步监视器的执行过程(底层使用的是monitor enter和monitor exit)
1.第一个线程访问,锁定同步监视器,执行其中代码
2.第二个线程访问,发现同步监视器被锁定,无法访问
3.第一个线程执行完毕,解锁同步监视器
4.第二个线程访问,加锁并访问
死锁
互斥条件:一个资源只能被一个进程使用
请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放
不可剥夺:进程已获得的资源,在未使用完前,不能强行剥夺
循环等待:若干进程之间形成一种头尾相接的循环等待资源关系
死锁实例:
public class DeadLock implements Runnable{
public boolean flag = true;
private static Object o1 = new Object();
private static Object o2 = new Object();
@Override
public void run() {
if(flag){
synchronized (o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("已经获取到1,准备获取到2");
synchronized (o2){
System.out.println("1获取2");
}
}
}else{
synchronized (o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("已经获取到2,准备获取到1");
synchronized (o1){
System.out.println("2获取1");
}
}
}
}
public static void main(String[] args) {
DeadLock dc1 = new DeadLock();
dc1.flag = true;
DeadLock dc2 = new DeadLock();
dc2.flag = false;
new Thread(dc1).start();
new Thread(dc2).start();
}
}
Lock接口:是java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock类实现了Lock接口,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
synchronized与Lock的对比
Lock是显式锁,synchronized是隐式的锁,作用域之外自动释放
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好
使用优先级顺序:Lock>同步代码块>同步方法
生产者消费者问题
线程之间的通信问题,需要线程之间同步处理,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。生产者和消费者之间,仅有synchronized是不够的。
synchronized可阻止并发更新同一个共享资源,实现了同步
synchronized不能用来实现不同线程之间的消息传递
Object的方法解决线程之间的通信问题
方法名 | 作用 |
---|---|
wait() | 表示线程一致等待,直到其他线程通知,与sleep不同,会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个处于等待状态的现场呢过 |
notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度 |
解决方式1:管程法
生产者:负责生产数据的模块
消费者:负责处理数据的模块
缓冲区:消费者不能直接使用生产者的数据,因此引入一个缓冲区
public class TestQuestion {
public static void main(String[] args) {
SynContainer sy = new SynContainer();
new Productor(sy).start();
new Consumer(sy).start();
new Consumer(sy).start();
new Consumer(sy).start();
}
}
//生产者线程类
class Productor extends Thread{
SynContainer container;
public Productor(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("消费者消费了第"+container.pop().id+"只鸡");
}
}
}
//产品
class Chicken{
public int id;
public Chicken(int id){
this.id = id;
}
}
//缓冲区
class SynContainer{
//容器
Chicken[] chick = new Chicken[10];
//容器计数器
int count = 0;
//生产者放产品
public synchronized void push(Chicken chickpara){
//如果容器满了,就需要等待消费者消费
while(count==chick.length){
//通知消费者消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,就将产品放入容器中
chick[count] = chickpara;
count++;
//通知消费者消费
this.notifyAll();
}
//消费者消费
public synchronized Chicken pop(){
while(count==0){
//消费者等待生产者生产商品
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken = chick[count];
//通知生产者生产
this.notifyAll();
return chicken;
}
}
解决方式2:信号量法
采用一个标志位,标志位不同时通知不同的线程做事
public class TestQuestion02 {
public static void main(String[] args) {
TVshow tVshow = new TVshow();
new Player(tVshow).start();
new Watcher(tVshow).start();
new Watcher(tVshow).start();
new Watcher(tVshow).start();
}
}
//生产者(演员)
class Player extends Thread{
TVshow tv;
public Player(TVshow 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{
TVshow tv;
public Watcher(TVshow tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.tv.watch();
}
}
}
//产品(节目)
class TVshow{
//演员表演,观众等待
//观众观看,演员等待
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 = !flag;
}
public synchronized void watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了:"+voice);
this.notifyAll();
this.flag = !flag;
}
}
线程池
线程的创建与销毁、使用量比较大,对性能的影响很大。
ExecutorService pool = Executors.newFixedThreadPool(10);
线程池的好处:
1.提高响应速度(减少了创建线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次创建)
3.便于线程管理
corePoolSize:核心线程池的大小
maximunPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
线程池相关的类:ExecutorService和Executors
ExecutorService:线程池接口,常见的子类有ThreadPoolExecutor
void execute(Runnable command): 执行任务/命令,没有返回值,一般用来执行Runnable
Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
void shutdown():关闭连接池
Executor:工具类、线程池的工具类,用于创建并返回不同类型的线程池
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建一个池子,大小为10
ExecutorService pool = Executors.newFixedThreadPool(10);
//执行
pool.execute(new MyThread());
pool.execute(new MyThread());
pool.execute(new MyThread());
pool.execute(new MyThread());
//关闭连接
pool.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}
基础的知识,反复学习,更加牢固