线程


线程的基本概念

  • 程序 :是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

  • 进程 :是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。—— 生命周期

    • 程序是静态的,进程是动态的。
    • 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。
  • 线程 :进程可进一步细化为线程,是一个程序内部的一条执行路径。

    • 若一个进程同一时间并行执行多个线程,就是支持多线程的
    • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(PC),线程切换的开销小
    • 一个进程中的多个线程共享相同的内存单元/内存地址空间→它们从同一堆中分配对象,可以访问相同的变量和对象,这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全隐患。
  • 并行&并发

    • 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
    • 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人同时做同一件事。
  • 线程是进程的一条独立的执行路径。
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程、gc线程
  • main()称之为主线程,为系统的入口,用于执行整个程序。
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度。调度器是与操作系统紧密相关的,先后顺序是不能人为干预的。
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
  • 线程会带来额外的开销,如cpu调度时间、并发控制开销。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

线程的创建和使用

3种方式:Thread类、Runnable接口Callable接口

1、Thread类

官方文档:

  1. 继承Thread类
  2. 重写run() 方法
  3. 创建线程对象,调用start()方法启动线程

继承Thread创建线程

线程不一定立即执行,CPU调度安排。

//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
//注意:线程开启不一定立即执行,由CPU调度执行
public class TestThread1 extends Thread{@Overridepublic void run() {//run方法线程体for (int i = 0;i < 20;i++){System.out.println("代码编写中......" + i);}}public static void main(String[] args) {//main线程,主线程//创建一个线程对象TestThread1 testThread1 = new TestThread1();//调用start()方法开启线程testThread1.start();for (int i = 0;i < 200;i++){System.out.println("多线程学习中......" + i);}}
}
/*--------------------------------------------------------------------------------------------
结果:
......
多线程学习中......135
多线程学习中......136
多线程学习中......137
多线程学习中......138
多线程学习中......139
多线程学习中......140
多线程学习中......141
多线程学习中......142
多线程学习中......143
多线程学习中......144
多线程学习中......145
多线程学习中......146
多线程学习中......147
多线程学习中......148
代码编写中......0
代码编写中......1
代码编写中......2
代码编写中......3
代码编写中......4
代码编写中......5
代码编写中......6
代码编写中......7
代码编写中......8
代码编写中......9
代码编写中......10
代码编写中......11
代码编写中......12
代码编写中......13
代码编写中......14
代码编写中......15
代码编写中......16
代码编写中......17
代码编写中......18
代码编写中......19
多线程学习中......149
多线程学习中......150
多线程学习中......151
多线程学习中......152
多线程学习中......153
多线程学习中......154
多线程学习中......155
多线程学习中......156
多线程学习中......157
多线程学习中......158
多线程学习中......159
多线程学习中......160
多线程学习中......161
多线程学习中......162
多线程学习中......163
......
*/

案例:多线程下载图片

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;}@Overridepublic 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://boot-img.xuexi.cn/lego/image/1564_first-slider/b75e038b2a3242f489c0066e2d6c7ccb.jpg","xdd1.jpg");TestThread2 t2 = new TestThread2("https://boot-img.xuexi.cn/lego/image/1564_first-slider/0c7666b9588e4d0a919ec34547ae031c.jpg","xdd2.jpg");TestThread2 t3 = new TestThread2("https://boot-img.xuexi.cn/lego/image/1564_first-slider/9c9dbdd5d8964ab7a29e0312fc1781b7.jpg","xdd3.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方法出现问题");}}
}
/*--------------------------------------------------------------------------------------------
结果:
下载了文件名为:xdd1.jpg
下载了文件名为:xdd3.jpg
下载了文件名为:xdd2.jpg
*/

2、Runnable接口

  1. 实现Runnable接口
  2. 重写run()方法
  3. 创建线程对象,执行线程需要丢入Runnable接口实现类调用start()方法启动线程。
//创建线程方式二:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法。
public class TestThread3 implements Runnable{@Overridepublic void run() {//run方法线程体for (int i = 0;i < 20;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 < 200;i++){System.out.println("多线程学习中......" + i);}}
}
/*--------------------------------------------------------------------------------------------
结果:
多线程学习中......0
多线程学习中......1
多线程学习中......2
代码编写中......0
代码编写中......1
代码编写中......2
代码编写中......3
代码编写中......4
代码编写中......5
代码编写中......6
代码编写中......7
代码编写中......8
代码编写中......9
代码编写中......10
代码编写中......11
代码编写中......12
代码编写中......13
代码编写中......14
代码编写中......15
代码编写中......16
代码编写中......17
代码编写中......18
代码编写中......19
多线程学习中......3
多线程学习中......4
多线程学习中......5
多线程学习中......6
多线程学习中......7
多线程学习中......8
多线程学习中......9
多线程学习中......10
多线程学习中......11
多线程学习中......12
多线程学习中......13
......
*/

3、继承Thread类和实现Runnable接口的对比

继承 Thread类

  • 子类继承Thread类具备多线程能力。
  • 启动线程:子类对象调用start()方法。
  • 不建议使用:避免OOP单继承局限性。

实现 Runnable接口

  • 实现接口Runnable具有多线程能力。
  • 启动线程:传入目标对象 + Thread对象调用start()方法。
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。

4、初识并发问题

多个线程操作同一个资源的情况下,线程不安全,数据紊乱。

//多个线程同时操作同一个对象
//买火车票//发现问题(线程并发问题):多个线程操作同一个资源的情况下,,线程不安全,数据紊乱。
public class TestThread4 implements Runnable {//票数private int ticketNums = 10;@Overridepublic 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();}
}
/*--------------------------------------------------------------------------------------------
结果:
粉丝-->拿到第10票
黄牛-->拿到第9票
站姐-->拿到第8票
黄牛-->拿到第7票   #黄牛拿到了第7张票
站姐-->拿到第6票
粉丝-->拿到第7票   #粉丝也拿到了第7张票
站姐-->拿到第5票
黄牛-->拿到第4票
粉丝-->拿到第3票
粉丝-->拿到第2票
黄牛-->拿到第1票
站姐-->拿到第0票
粉丝-->拿到第-1票   #出现了-1
*/

龟兔赛跑

public class TestThread5 implements Runnable{//保证胜利者的唯一性private static String winner;@Overridepublic void run() {for (int i = 1;i <= 100;i++){//模拟兔子休息if (Thread.currentThread().getName().equals("兔子") && i%10==0){try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}//模拟乌龟慢跑if (Thread.currentThread().getName().equals("乌龟") && i%1==0){try {Thread.sleep(1);} 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;}if (steps >= 100){winner = Thread.currentThread().getName();System.out.println("winner is " + winner);return true;}return false;}public static void main(String[] args) {TestThread5 testThread5 = new TestThread5();new Thread(testThread5,"兔子").start();new Thread(testThread5,"乌龟").start();}
}
/*--------------------------------------------------------------------------------------------
结果:
兔子-->跑了1步
乌龟-->跑了1步
兔子-->跑了2步
兔子-->跑了3步
兔子-->跑了4步
兔子-->跑了5步
兔子-->跑了6步
兔子-->跑了7步
兔子-->跑了8步
兔子-->跑了9步
乌龟-->跑了2步
乌龟-->跑了3步
乌龟-->跑了4步
乌龟-->跑了5步
乌龟-->跑了6步
乌龟-->跑了7步
乌龟-->跑了8步
乌龟-->跑了9步
乌龟-->跑了10步
乌龟-->跑了11步
乌龟-->跑了12步
乌龟-->跑了13步
乌龟-->跑了14步
乌龟-->跑了15步
乌龟-->跑了16步
乌龟-->跑了17步
乌龟-->跑了18步
乌龟-->跑了19步
乌龟-->跑了20步
乌龟-->跑了21步
乌龟-->跑了22步
乌龟-->跑了23步
乌龟-->跑了24步
乌龟-->跑了25步
乌龟-->跑了26步
乌龟-->跑了27步
乌龟-->跑了28步
乌龟-->跑了29步
乌龟-->跑了30步
乌龟-->跑了31步
乌龟-->跑了32步
乌龟-->跑了33步
乌龟-->跑了34步
乌龟-->跑了35步
乌龟-->跑了36步
乌龟-->跑了37步
乌龟-->跑了38步
乌龟-->跑了39步
乌龟-->跑了40步
乌龟-->跑了41步
乌龟-->跑了42步
乌龟-->跑了43步
乌龟-->跑了44步
乌龟-->跑了45步
乌龟-->跑了46步
乌龟-->跑了47步
乌龟-->跑了48步
乌龟-->跑了49步
兔子-->跑了10步
兔子-->跑了11步
兔子-->跑了12步
兔子-->跑了13步
兔子-->跑了14步
兔子-->跑了15步
兔子-->跑了16步
兔子-->跑了17步
兔子-->跑了18步
兔子-->跑了19步
乌龟-->跑了50步
乌龟-->跑了51步
乌龟-->跑了52步
乌龟-->跑了53步
乌龟-->跑了54步
乌龟-->跑了55步
乌龟-->跑了56步
乌龟-->跑了57步
乌龟-->跑了58步
乌龟-->跑了59步
乌龟-->跑了60步
乌龟-->跑了61步
乌龟-->跑了62步
乌龟-->跑了63步
乌龟-->跑了64步
乌龟-->跑了65步
乌龟-->跑了66步
乌龟-->跑了67步
乌龟-->跑了68步
乌龟-->跑了69步
乌龟-->跑了70步
乌龟-->跑了71步
乌龟-->跑了72步
乌龟-->跑了73步
乌龟-->跑了74步
乌龟-->跑了75步
乌龟-->跑了76步
乌龟-->跑了77步
乌龟-->跑了78步
乌龟-->跑了79步
乌龟-->跑了80步
乌龟-->跑了81步
乌龟-->跑了82步
乌龟-->跑了83步
乌龟-->跑了84步
乌龟-->跑了85步
乌龟-->跑了86步
乌龟-->跑了87步
乌龟-->跑了88步
乌龟-->跑了89步
乌龟-->跑了90步
乌龟-->跑了91步
乌龟-->跑了92步
乌龟-->跑了93步
乌龟-->跑了94步
乌龟-->跑了95步
乌龟-->跑了96步
乌龟-->跑了97步
乌龟-->跑了98步
乌龟-->跑了99步
兔子-->跑了20步
winner is 乌龟
*/

5、实现Callable接口

  1. 实现Callable接口,需要返回值类型
  2. 重写Call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future<Boolean> result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();

通过实现Callable接口创建线程的好处

  • 可以定义返回值
  • 可以抛出异常
//线程创建方式三:实现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;}@Overridepublic 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://boot-img.xuexi.cn/lego/image/1564_first-slider/b75e038b2a3242f489c0066e2d6c7ccb.jpg","xdd1.jpg");TestCallable t2 = new TestCallable("https://boot-img.xuexi.cn/lego/image/1564_first-slider/0c7666b9588e4d0a919ec34547ae031c.jpg","xdd2.jpg");TestCallable t3 = new TestCallable("https://boot-img.xuexi.cn/lego/image/1564_first-slider/9c9dbdd5d8964ab7a29e0312fc1781b7.jpg","xdd3.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();//打印返回值System.out.println(rs1);System.out.println(rs2);System.out.println(rs3);//关闭服务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方法出现问题");}}
}
/*--------------------------------------------------------------------------------------------
结果:
下载了文件名为:xdd3.jpg
下载了文件名为:xdd1.jpg
下载了文件名为:xdd2.jpg
true
true
true
*/

6、Lambda表达式

  • 为什么要使用Lambda表达式

    • 避免匿名内部类定义过多。
    • 可以让代码看起来很简洁。
    • 去掉了一堆没有意义的代码,只留下核心的逻辑。

其实质属于函数式编程的概念。

函数式接口的定义:任何接口,如果只包含唯一一个抽象方法,那么就是一个函数式接口。比如Runnable接口:

public interface Runnable {public abstract void run();
}

对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。

lambda表达式的推导

1、接口 + 实现类

public class Testlambda2 {public static void main(String[] args) {ILove love = new Love();love.love(6);}
}//1.定义一个函数式接口
interface ILove{void love(int a);
}//2.实现类
class Love implements ILove{@Overridepublic void love(int a) {System.out.println("I love --> " + a);}
}

2、静态内部类

public class Testlambda2 {//3.静态内部类static class Love implements ILove{@Overridepublic void love(int a) {System.out.println("I love --> " + a);}}public static void main(String[] args) {ILove love = new Love();love.love(6); //调用静态内部类}
}
//1.定义一个函数式接口
interface ILove{void love(int a);
}

3、局部内部类

public class Testlambda2 {public static void main(String[] args) {//4.局部内部类class Love implements ILove{@Overridepublic void love(int a) {System.out.println("I love --> " + a);}}//调用ILove love = new Love();love.love(6);}
}//1.定义一个函数式接口
interface ILove{void love(int a);
}

4、匿名内部类

public class Testlambda2 {public static void main(String[] args) {//5.匿名内部类:没有类的名称,必须借助接口或者父类ILove love = new ILove() {@Overridepublic void love(int a) {System.out.println("I love --> " + a);}};love.love(6);}
}//1.定义一个函数式接口
interface ILove{void love(int a);
}

5、lambda表达式简化

public class Testlambda2 {public static void main(String[] args) {//6.用lambda简化ILove love = (int a) -> {System.out.println("I love --> " + a);};love.love(6);}
}//1.定义一个函数式接口
interface ILove{void love(int a);
}

6、带参数的lambda表达式

public class Testlambda2 {public static void main(String[] args) {ILove love = null;
/*        //1.lambda表达式简化ILove love = (int a) -> {System.out.println("I love --> " + a);};//简化1.去参数类型love = (a) -> {System.out.println("I love --> " + a);};//简化2.去括号love = a -> {System.out.println("I love --> " + a);};*///简化3.去花括号love = a -> System.out.println("I love --> " + a);love.love(66);}
}
//1.定义一个函数式接口
interface ILove{void love(int a);
}
  • 总结:

    • lambda表达式只能有一行代码的情况下,才能去掉大括号,简化成一行。多行,必须使用代码块包括。
    • lambda表达式前提必须是函数式接口
    • 多个参数也可以去掉参数类型,要去掉都去掉,但必须加上括号

7、静态代理

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{@Overridepublic void happyMarry() {System.out.println("我要结婚了,超开心!");}
}
//代理角色:帮助你结婚,婚庆公司
class WeddingCompany implements Marry{//代理真实角色:目标对象private Marry target;public WeddingCompany(Marry target) {this.target = target;}@Overridepublic void happyMarry() {before();// 真实对象this.target.happyMarry();after();}private void before() {System.out.println("结婚之前,布置现场");}private void after() {System.out.println("结婚之后,收尾款");}
}
/*--------------------------------------------------------------------------------------------
结果:
结婚之前,布置现场
我要结婚了,超开心!
结婚之后,收尾款
*/

总结:

  • 真实对象和代理对象都要实现同一个接口
  • 代理对象要代理真实角色
  • 代理对象可以做很多真实对象做不了的事情
  • 真实对象专注做自己的事情

静态代理和线程创建的对比:

实现Runnable接口创建线程的启动,底层原理就是静态代理

public class StaticProxy {public static void main(String[] args) {new Thread(() -> {System.out.println("我爱你");}).start();new WeddingCompany(new You()).happyMarry();//简写}
}

8、线程状态(五大状态)


9、线程方法

方法 说明
setPriority(int newPriority) 设置线程的优先级。
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程体休眠。不建议使用Thread.sleep()方法使用TimeUnit下的seelp方法。
void join() 强制执行线程。可以理解为插队。
static void yield() 礼让。暂停当前正在执行的线程对象,并执行其他线程。不一定礼让成功。
void interrupt() 中断线程,别用这个方式
boolean isAlive() 测试线程是否处于活动状态 。

10、停止线程

  • 不推荐使用JDK提供的stop()、destroy()方法。【Deprecated 表示已废弃】

  • 推荐线程自己停止下来

  • 建议使用一个标志位进行终止变量。当flag=false,则终止线程运行。

//测试stop
//1.建议线程正常停止 --> 利用次数停止,不建议死循环
//2.建议使用标志位 --> 设置一个标志位
//3.不要使用stop或者destroy等过时 或者 JDK不建议使用的方法
public class Teststop implements Runnable {//1.设置一个标志位private boolean flag = true;@Overridepublic void run() {int i = 0;while (flag){System.out.println("Running 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){//调用stop方法切换标志位,让线程停止teststop.stop();System.out.println("该线程停止运行!");}}}
}

11、线程休眠 - sleep

  • sleep(时间)指定当前线程阻塞的毫秒数
  • seep存在异常InterruptedException
  • sleep时间达到后线程进入就绪状态
  • seep可以模拟网络延时,倒计时等。
  • 每一个对象都有一个锁,sleep不会释放锁。

**模拟网络延时是为了放大问题的发生性。 **

模拟倒计时

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;}}}
}

打印系统当前时间

public class TestSleep2 {public static void main(String[] args) {Date date = new Date(System.currentTimeMillis());while (true){try {Thread.sleep(1000);System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));// 更新当前事件date = new Date(System.currentTimeMillis());} 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;}}}
}

12、线程礼让 - yield

礼让线程,让当前正在执行的线程暂停,但不阻塞

将线程从运行状态转为就绪状态

让CPU重新调度礼让不一定成功 。看CPU心情。

public class TestYield {public static void main(String[] args) {MyYield myYield = new MyYield();new Thread(myYield,"Alice").start();new Thread(myYield,"Bob").start();}
}class MyYield implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"线程开始执行...");Thread.yield(); //礼让System.out.println(Thread.currentThread().getName()+"线程停止执行!");}
}
/*--------------------------------------------------------------------------
结果:
Bob线程开始执行...
Alice线程开始执行...
Bob线程停止执行!
Alice线程停止执行!
*/

13、线程强制执行 - join

Join合并线程,待此线程执行完成后,在执行其他线程,容易造成线程阻塞,不建议使用。

//测试Join方法
//可理解为插队
public class TestJoin implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) { //快捷输入100.forSystem.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 < 300; i++) {if (i==200){thread.join(); //插队,强制执行}System.out.println("main" + i);}}
}

14、线程状态观测

Thread. State

线程状态。线程可以处于以下状态之一:

  • NEW 尚未启动的线程处于此状态
  • RUNNABLE 在Java虚拟机中执行的线程处于此状态。
  • BLOCKED 被阻塞等待监视器锁定的线程处于此状态
  • WAITING 正在等待另一个线程执行特定动作的线程处于此状态
  • TIMED WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
  • TERMINATED 已退出的线程处于此状态。

一个线程可以在给定时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。

死亡的线程无法再次启动!!!

//观测线程状态
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(2000);} 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(1000);state = thread.getState();//更新线程状态System.out.println(state);//输出状态}}
}

15、线程优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。

线程的优先级用数字表示,范围从1~10

  • Thread. MIN_PRIORTY=1
  • Thread. MAX_PRIORITY= 10
  • Thread. NORM_PRIORITY= 5

使用以下方式改变或获取优先级

  • getPriority()
  • setPriority(int xxx)

先设置优先级,再启动线程。

//测试线程的优先级
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();//默认优先级:5t2.setPriority(1); //优先级的设定最好写在start()调度前t2.start();t3.setPriority(4);t3.start();t4.setPriority(Thread.MAX_PRIORITY); //最高默认优先级:MAX_PRIORITY = 10t4.start();t5.setPriority(Thread.MIN_PRIORITY); //最低默认优先级:MIN_PRIORITY = 1t5.start();t6.setPriority(9);t6.start();}
}class MyPriority implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());}
}
/*--------------------------------------------------------------------------------------------
结果:
main-->5
Thread-0-->5
Thread-1-->1
Thread-2-->4
Thread-3-->10
Thread-4-->1
Thread-5-->9
*/

优先级低只是意味着获得调度的概率低。并不是优先级低就不会被调用了。这都是看CPU的调度

16、守护线程 - daemon

  • 线程分为用户线程守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
    • 如:后台记录操作日志、监控内存、垃圾回收等待。
//测试守护线程
public class TestDeamon {public static void main(String[] args) {God god = new God();Your your = new Your();Thread thread = new Thread(god);thread.setDaemon(true); //默认是false表示是用户线程,正常的线程都是用户线程...thread.start(); //启动守护线程new Thread(your).start();  //启动用户线程}
}
//上帝
class God implements Runnable{@Overridepublic void run() {while (true){System.out.println("上帝保佑着你!");}}
}
//你
class Your implements Runnable{@Overridepublic void run() {for (int i = 0; i < 36500; i++) {System.out.println("开心的活着!");}System.out.println("GoodBye!World!");}
}

17、线程同步机制

并发

多个线程操作同一个资源

并发:同一个对象多个线程同时操作。比如:上万人同时抢100张票、两个银行同时取钱等。

  • 现实生活中我们会遇到”同一个资源,多个人都想使用“的问题。比如食堂排队打饭,每个人都想吃饭,最天然的解决办法就是排队。【队列】
  • 处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。这时候我们就需要线程同步。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

队列和锁

形成条件:队列 + 锁(每个对象都拥有一把锁)

线程同步

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题。为了保证数据在方法中被访问时的正确性,在访问时加入锁机制

synchronized(线程同步) 当一个线程获得对象的排它锁,独占资源,其他线程必须等待使用后释放锁即可。

  • 存在以下问题:

    • 一个线程持有锁会导致其他所有需要此锁的线程挂起
    • 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题。

18、三大线程不安全案例

买票

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;@Overridepublic 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
牛逼的你们拿到了第-1张票
Process finished with exit code 0
// 多个人拿一张票
牛逼的你们拿到了第2张票
可恶的黄牛党拿到了第2张票
*/

测试发现有多个人拿到同一张票和出现了负数,线程不安全

每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

银行取钱

public class UnsafeBank {public static void main(String[] args) {Account account = new Account(100,"结婚基金");Drawing you = new Drawing(account, 50,"你");Drawing girlFriend = new Drawing(account, 100,"girlFriend");you.start();girlFriend.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;}//取钱@Overridepublic 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);}
}
/*--------------------------------------------------------------------------
结果1:
结婚基金余额为:50
你手里的钱:50
结婚基金余额为:-50
girlFriend手里的钱:100结果2:
结婚基金余额为:-50
结婚基金余额为:-50
girlFriend手里的钱:100
你手里的钱:50
*/

线程不安全的集合

同时有两个或者多个线程一瞬间同时操作了同一个位置,也就是把多个数据添加到了同一个位置,会覆盖掉,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(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}
/*--------------------------------------------------------------------------
结果:
9999
*/

19、同步方法及同步块

由于我们可以通过 private关键字来保证数据对象只能被方法访问。所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字。它包括两种用法**:synchronized方法**和 synchronized块

锁要增删改查的对象

同步方法(只能锁 this)

同步方法: public synchronized void method(int args){}

synchronized方法控制对对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞。即方法一旦执行,就独占该锁,直到该方法返回才释放锁。后面被阻塞的线程才能获得这个锁,继续执行。

缺陷:若将一个大的方法申明为synchronized将会影响效率

方法里面需要修改的内容才需要锁,锁得太多,浪费资源。

同步块(可以锁任何对象)

同步块:synchronized(obj){}

obj称之为同步监视器

  • obj可以是任何对象,但是推荐使用共享资源作为同步监视器
  • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class[反射中讲解]。

同步监视器的执行过程

  1. 第一个线程访问,锁定同步监视器,执行其中代码。
  2. 第二个线程访问,发现同步监视器被锁定,无法访问。
  3. 第一个线程访问完毕,解锁同步监视器。
  4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问。

安全的买票

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;@Overridepublic void run() {// 买票while (flag){try {buy();Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}//synchronized 同步方法。锁得是thisprivate synchronized void buy() throws InterruptedException {// 判断是否有票if (ticketNums <= 0){flag = false;return;}// 买票System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");}
}
/*--------------------------------------------------------------------------
结果:
苦逼的我拿到了第10张票
可恶的黄牛党拿到了第9张票
牛逼的你们拿到了第8张票
苦逼的我拿到了第7张票
可恶的黄牛党拿到了第6张票
牛逼的你们拿到了第5张票
苦逼的我拿到了第4张票
可恶的黄牛党拿到了第3张票
牛逼的你们拿到了第2张票
苦逼的我拿到了第1张票
*/

安全的取钱

public class UnsafeBank {public static void main(String[] args) {Account account = new Account(1000,"结婚基金");Drawing you = new Drawing(account, 50,"你");Drawing girlFriend = new Drawing(account, 100,"girlFriend");you.start();girlFriend.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;}// 取钱@Overridepublic 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);System.out.println(this.getName()+"手里的钱:" + nowMoney);}}
}
/*--------------------------------------------------------------------------
结果1:
结婚基金余额为:950
你手里的钱:50
结婚基金余额为:850
girlFriend手里的钱:100结果2:
结婚基金余额为:900
girlFriend手里的钱:100
结婚基金余额为:850
你手里的钱:50
*/

安全的集合

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());}
}
/*--------------------------------------------------------------------------
结果:
10000
*/

当去掉延时的时候,发现打印的小于10000。这是因为打印语句是由主线程执行的,主线程虽然执行完了(电脑性能太好),但是往集合中添加元素的线程还没有执行完。这样打印结果就小于10000了。但是此时还是线程安全。

20、CopyOnWriteArrayList

  • 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(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}
/*--------------------------------------------------------------------------
结果:
10000
*/

21、死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行。从而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有两个以上对象的锁时,就可能会发生死锁的问题。

多个线程互相抱着对方需要的资源,然后形成僵持。

//死锁:多个线程互相抱着对方需要的资源,然后形成僵持。
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;public Makeup(int choice, String girlName){this.choice = choice;this.girlName = girlName;}@Overridepublic 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);// 1秒钟后想获得镜子synchronized (mirror){System.out.println(this.girlName + "获得镜子的锁");}}} else {synchronized (mirror){System.out.println(this.girlName + "获得镜子的锁");Thread.sleep(2000);// 1秒钟后想获得镜子synchronized (lipstick){System.out.println(this.girlName + "获得口红的锁");}}}}
}
/*--------------------------------------------------------------------------
测试会发现程序卡死:灰姑凉获得口红的锁
白雪公主获得镜子的锁
*/

修改makeUp()方法:

private void makeUp() throws InterruptedException {if (choice == 0){// 获得口红的锁synchronized (lipstick){System.out.println(this.girlName + "获得口红的锁");Thread.sleep(1000);}// 1秒钟后想获得镜子synchronized (mirror){System.out.println(this.girlName + "获得镜子的锁");}} else {synchronized (mirror){System.out.println(this.girlName + "获得镜子的锁");Thread.sleep(2000);}// 1秒钟后想获得镜子synchronized (lipstick){System.out.println(this.girlName + "获得口红的锁");}}
}
/*--------------------------------------------------------------------------
结果:
灰姑凉获得口红的锁
白雪公主获得镜子的锁
白雪公主获得口红的锁
灰姑凉获得镜子的锁
*/

产生死锁的四个必要条件

  • 互斥条件:一个资源每次只能被一个进程使用。
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

上面列出了死锁的四个必要条件,我们只要想办法破坏其中的任意一个或多个条件就可以避免死锁发生。

22、Lock锁

class A implements Runnable{//定义lock锁private  final ReentrantLock lock = new ReentrantLock();public void run() {try {//加锁lock.lock();//保证线程安全的代码    }finally {//解锁lock.unlock();//如果同步代码有误,要将unlock()写入finally语句块}}
}

从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。

java. util. concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。

ReentrantLock类(可重入锁)实现了Lock,它拥有与 synchronized相同的并发性和内存语义。在实现线程安全的控制中,比较常用的是 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 tickerNums = 10;//定义lock锁private  final ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true){try {//加锁lock.lock();if (tickerNums > 0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(tickerNums--);}else {break;}}finally {//解锁lock.unlock();}}}
}

synchronized与Lock的对比

  • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)。synchronized是隐式锁,出了作用域自动释放。
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好(可靠性更高)。并且具有更好的扩展性(提供更多的子类)。
  • 优先使用顺序
    • Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)

23、线程协作 - 生产者消费者问题

应用场景:生产者和消费者问题

  • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。
  • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。
  • 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。

这是一个线程同步问题:生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。

  • 对于生产者,没有生产产品之前,要通知消费者等待。而生产了产品之后,又需要马上通知消费者消费。
  • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费。
  • 在生产者消费者问题中,仅有 synchronized是不够的:
    • synchronized可阻止并发更新同一个共享资源,实现了同步。
    • synchronized不能用来实现不同线程之间的消息传递(通信)。

Java提供了几个方法解决线程之间的通信问题

方法名 作用
wait() 表示线程一直等待,直到其他线程通知,与seep不同。会释放锁
wait(long timeout) 指定等待的毫秒数。
notify() 唤醒一个处于等待状态的线程。
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级比较高的线程优先调度。

注意:均是 Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorState Exception。

24、管程法

并发协作模型“生产者/消费者模式” --> 管程法

  • 生产者:负责生产数据的模块(可能是方法、对象、线程、进程)。
  • 消费者:负责处理数据的模块(可能是方法、对象、线程、进程)。
  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区。

生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据。

管程法:生产者、消费者、产品、缓冲区

public class TestPC {public static void main(String[] args) {// 容器SynContainer container = new SynContainer();// 生产者new Productor(container).start();// 消费者new Consumer(container).start();}
}//生产者
class Productor extends Thread{SynContainer container;public Productor(SynContainer container){this.container = container;}//生产@Overridepublic 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;}//消费@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("消费了===>第" + container.pop().id + "只鸡");}}
}//产品
class Chicken extends Thread{//产品编号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;count++;// 可以通知消费者消费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;}
}
/*--------------------------------------------------------------------------
结果:
生产了==>第0只鸡
生产了==>第1只鸡
生产了==>第2只鸡
生产了==>第3只鸡
生产了==>第4只鸡
生产了==>第5只鸡
生产了==>第6只鸡
生产了==>第7只鸡
生产了==>第8只鸡
生产了==>第9只鸡
生产了==>第10只鸡
消费了===>第4只鸡
生产了==>第11只鸡
消费了===>第10只鸡
生产了==>第12只鸡
*/

25、信号灯法

信号灯法,标志位解决。

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;}@Overridepublic 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;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {this.tv.watch();}}
}// 产品 --> 节目
class Tv{// 演员表演,观众等待 T// 观众观看,演员等待 F// 节目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;}
}
/*--------------------------------------------------------------------------
结果:
演员表演了《大本营播放中》
观看了《大本营播放中》
演员表演了《抖音:记录美好生活》
观看了《抖音:记录美好生活》
演员表演了《大本营播放中》
观看了《大本营播放中》
演员表演了《抖音:记录美好生活》
观看了《抖音:记录美好生活》
*/

26、线程池

使用线程池的原因

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大

思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

好处:

  • 提高响应速度(减少了创建新线程的时间)。
  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)。
  • 便于线程管理。
    • corePoolsize:核心池的大小。
    • maximumPoolSize:最大线程数。
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止。

使用线程池

  • JDK5.0起提供了线程池相关API:ExecutorServiceExecutors

  • ExecutorService:真正的线程池接口。常见子类 ThreadPoolExecutor

    • void execute( Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable。
    • Future submit(Callable task):执行任务,有返回值,一般用来执行Callable。
    • void shutdown():关闭连接池。
  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池。

public class TestPool {public static void main(String[] args) {// 1、创建服务,创建线程池.参数为线程池大小ExecutorService service = Executors.newFixedThreadPool(10);// 2、执行service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());// 3、关闭连接service.shutdown();}
}
class MyThread implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}
/*--------------------------------------------------------------------------
结果:
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
*/

总结

//回顾总结线程的创建
public class ThreadNew {public static void main(String[] args) {new MyThread1().start();new Thread(new MyThread2()).start();FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());new Thread(futureTask).start();try {Integer integer = futureTask.get();System.out.println(integer);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}//1.继承Thread类
class MyThread1 extends Thread{@Overridepublic void run() {System.out.println("MyThread1");}
}//2.实现Runnable接口
class MyThread2 implements Runnable{@Overridepublic void run() {System.out.println("MyThread2");}
}//3.实现Callable接口
class MyThread3 implements Callable<Integer>{@Overridepublic Integer call() throws Exception {System.out.println("MyThread3");return 100;}
}

Java学习笔记——线程相关推荐

  1. Java学习笔记----线程

    继续学习----------------------------------------------------------------------- 在Java中线程指的是两件事 1)java.la ...

  2. Java学习笔记-线程中countDown()使用

    1.解释 等待其他线程处理完才继续当前线程.比如要要执行B线程,前面有A线程,必须要把A线程执行完,才可以执行B线程. 某一线程在开始运行前等待n个线程执行完毕.将CountDownLatch的计数器 ...

  3. java学习笔记15--多线程编程基础2

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...

  4. java学习笔记14--多线程编程基础1

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note14.html,转载请注明源地址. 多线程编程基础 多进程 一个独立程序的每一次运行称为 ...

  5. Java学习笔记:创建线程的两种方法

    Java学习笔记:创建线程的两种方法 一.预备工作 1.创建Maven项目ThreadDemo 2.在pom.xml里添加依赖 二.继承Thread类创建子线程

  6. java学习笔记13--反射机制与动态代理

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...

  7. java学习笔记16--I/O流和文件

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note16.html,转载请注明源地址. IO(Input  Output)流 IO流用来处理 ...

  8. java学习笔记11--集合总结

    java学习笔记系列: java学习笔记10--泛型总结 java学习笔记9--内部类总结 java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Ob ...

  9. Java学习笔记(7)——Java基础之IO多线程网络思维导图

    Java面向对象学习笔记之:包括IO(字节流,字符流,节点流,处理流).线程(线程创建,线程控制,线程同步).网络(TCP Scoket,  UDP Scoket)(全屏观看Java学习笔记(7)-- ...

最新文章

  1. Redis的KEYS命令引起宕机事件
  2. word导入中的一个乱码
  3. 轻量级定时任务框架:APScheduler
  4. 简单的多显示器解决方案
  5. linux常用操作命令汇总
  6. jquery的ajax,请求JSON数据。
  7. redis最基础的入门教程
  8. Java序列化后isXXX变量没有了
  9. 使用Sklearn模型做分类并绘制机器学习模型的ROC曲线
  10. cpu vtx测试软件,推荐几个好用的检测电脑CPU是否支持(Virtualization Technology)虚拟化技术的工具-推荐实用小软件 -亦是美网络...
  11. 性能测试---影响性能的因素
  12. Android 中Goolgle 相关服务的移植
  13. 解决ios7.x越狱后静态壁纸变为空白
  14. k8s(四)—service服务(暴露方式:ClusterIP、NodePort、LoadBalancer、ExternalName、ingress)
  15. Jmeter 阶梯线程组
  16. Typora、Markdown中的公式,颜色汇总
  17. 独立开发者接入防沉迷尽然如此简单?-TapTap 防沉迷模块iOS版
  18. 在互联网公司大举进军移动支付和网络理财之际 平安推出壹钱包对战互联网“群狼”
  19. github视频录制播放相关功能-参考
  20. 最新目标检测算法回顾2022笔记

热门文章

  1. C#百度关键字指数查询Socket实现
  2. 童文红:阿里要靠什么活过102岁?
  3. 谷歌验证码 ReCAPTCHA 的模拟点击破解方案来了!
  4. 基于Sikuli GUI图像识别框架的PC客户端自动化测试实践
  5. mktime函数性能分析
  6. 风控策略精准运维的制胜点,一个重要却容易被轻视的内容
  7. Pornhub 风格的 Logo 制作器,你值得拥有!
  8. linux下分析hprof大文件,Hprof 内存分类分析
  9. Mob统计分析功能详解 精细化运营必备
  10. vux-upload组件