多线程秋招真的很常问,自己看B站视频笼统的过了一遍复习。
这个老师讲的想对比较适合初学,高阶建议啃书。😀
记了一些笔记写成博客分享出来。📕
在这推荐up主:狂神说。👍
指路b站视频链接:
https://www.bilibili.com/video/BV1V4411p7EF/
多线程 Java.Thread
P1 线程简介
P2 线程创建
初识并发
静态代理+lambda表达式
P3 线程状态
P4 线程同步
P5 线程通信问题
P6 线程池
P2 线程创建
初识并发
静态代理+lambda表达式
1.线程创建
Thread、Runnable、Callable
线程开启不一定立即执行,由CPU调度执行。
不一定按start()顺序执行。
①继承Thread类(Thread实现了Runnable接口)
step1:自定义线程类继承Thread类
step2:重写run()方法,编写线程执行体
step3:创建线程对象,调用start()方法启动线程
子类继承Thread类具备多线程能力
启动线程:子类对象.start()
不建议使用:避免OOP单继承局限性
//创建线程方式1:
// 继承Thread类,
// 重写run()方法,
// 调用start开启线程
public class TestThread1 extends Thread{
@Override
public 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 < 20; i++) {
System.out.println("在看主线程啊啊啊啊啊"+i);
}
}
}
②实现Runnable接口
step1:定义MyRunnable类实现Runnable接口
step2:重写run()方法,编写线程执行体
step3:创建线程对象,丢入runnable接口实现类(代理),调用start()方法启动线程
实现接口Runnable具有多线程能力
启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
//创建线程方式2:
// 实现runnable接口
// 重写run方法
// 执行线程需要丢入runnable接口实现类,调用start
public class TestThread2 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("在学习多线程"+i);
}
}
public static void main(String[] args) {
//main线程,主线程
//创建runnable接口的实现类对象
TestThread2 testThread2=new TestThread2();
//创建线程对象,通过线程对象开启线程,代理
//Thread thread=new Thread(testThread2);
//调用start()方法开启线程
//thread.start();
new Thread(testThread2).start(); //合并一行
for (int i = 0; i < 20; i++) {
System.out.println("在看主线程啊啊啊啊啊"+i);
}
}
}
③实现Callable接口 (了解)
step1:实现Callable接口,需要返回值类型
step2:重写call方法,需要抛出异常
step3:创建目标对象
step4:创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
step5:提交执行:Future r1= ser.submit(t1);
step6:获取结果:boolean result1 = r1.get()
step7:关闭服务:ser.shutdownNow();
callable好处
1.可以定义返回值
2.可以抛出异常
public class TestThread3 implements Callable<Boolean> {
@Override
public Boolean call() {
System.out.println(Thread.currentThread().getName()+" Callable线程!!!!");
return true;
}
public static void main(String[] args) throws ExecutionException,InterruptedException{
TestThread3 callThread1=new TestThread3();
TestThread3 callThread2=new TestThread3();
TestThread3 callThread3=new TestThread3();
//创建执行服务
ExecutorService ser= Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1=ser.submit(callThread1);
Future<Boolean> r2=ser.submit(callThread2);
Future<Boolean> r3=ser.submit(callThread3);
//获取结果
boolean result1=r1.get();
boolean result2=r2.get();
boolean result3=r3.get();
System.out.println(result1);
System.out.println(result2);
System.out.println(result3);
//关闭服务
ser.shutdown();
System.out.println("在看主线程啊啊啊啊啊");
}
}
2.初识并发 (买火车票+龟兔赛跑)
买火车票
多个线程操作同一个资源,线程不安全,数据紊乱
Thread.currentThread():获取当前线程的引用,既代码段正在被哪一个线程调用。
//多个线程操作同一个对象
//买火车票
//发现问题:
public class TestThread 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+"票");
ticketNums--;
}
}
public static void main(String[] args) {
TestThread ticketThread=new TestThread();
new Thread(ticketThread,"老师").start();
new Thread(ticketThread,"小明").start();
new Thread(ticketThread,"黄牛").start();
}
}
龟兔赛跑
1. 首先来个赛道距离,然后要离终点越来越近
2. 判断比赛是否结束
3. 打印出胜利者
4. 龟兔赛跑开始
5. 故事中是乌龟赢的,兔子需要睡觉,所以我们来模拟兔子睡觉
6. 终于,乌龟赢得比赛
//模拟龟兔赛跑
public class RaceThread implements Runnable{
//胜利者
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子休息
if(Thread.currentThread().getName().equals("兔子啊")){
try{
Thread.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flg=gameOver(i);
//比赛结束停止程序
if (flg){
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) {
RaceThread raceThread=new RaceThread();
new Thread(raceThread,"兔子啊").start();
new Thread(raceThread,"乌龟").start();
}
}
3.1 静态代理
实现静态代理对比Thread
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
好处:
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情
new Thread(()-> System.out.println("love")).start();
new WeddingCompany(new You()).HappyMarry();
Thread相当于一个代理,实现了接口Runnable,进行.start()。
婚庆公司代理模拟代码:
public class StaticProxy {
public static void main(String[] args) {
new Thread(()-> System.out.println("love")).start();
new WeddingCompany(new You()).HappyMarry();
//You you=new You();
//WeddingCompany weddingCompany=new WeddingCompany(you);
//wedingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//真实角色
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("you结婚超开心");
}
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
//代理 真实目标角色
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry(); //这就是真实对象
after();
}
private void before() {
System.out.println("结婚前,布置现场");
}
private void after() {
System.out.println("结婚之后,收尾款");
}
}
3.2 Lambda表达式 λ
λ希腊字母表中排序第十一位的字母,英语名称为Lambda
避免匿名内部类定义过多
其实质属于函数式编程的概念
(params) -> expression [ 表达式 ]
(params) -> statement [ 语句 ]
(params) -> { statements }
new Thread(()-> System.out.println("λ表达式")).start();
1.为什么使用lamdba表达式
避免内部类定义过多
让代码更简洁
去掉了没有意义的代码,只留下核心的逻辑
2.理解Functional Interface函数式接口
Runnable接口式函数式接口
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
对于函数式接口,我们可以通过lamdba表达式来表达创建该接口的对象。
public interface Runnable{
public abstract void run();
}
推导lambda表达式:
//推导lambda表达式
public class TestLambda_1 {
public static void main(String[] args) {
ILike like=new Like1(); //1.实现类
like.lambda();
like=new Like2(); //2.静态内部类
like.lambda();
//3.局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda 333");
}
}
like=new Like3(); //3.局部内部类
like.lambda();
//4.匿名内部类,没有类的名称,必须借助接口或者父类
like=new ILike() {
@Override
public void lambda() {
System.out.println("i like lambda 4444");
}
};
like.lambda(); //4.匿名内部类
//5.用lambda简化
like=()->{
System.out.println("i like lambda 55555");
};
like.lambda(); //5.用lambda简化 + 0.函数式接口
}
//2.静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda 22");
}
}
}
//0.定义一个函数式接口,只包含唯一一个抽象方法的接口
interface ILike{
void lambda();
}
//1.实现类
class Like1 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda 1");
}
}