多线程详解上(多线程详解(01.多线程概述02.线程、进程、多线程03.继承Thread类(重点)04.网图下载05.实现Runnable接口(重点)06.初识并发问题07.龟兔赛跑08.实现Callable接口09.静态代理模式10.Lambda表达式)
多线程(java.Thread)
01.多线程概述
02.线程、进程、多线程
1.普通方法和多线程
2.一个进程有多个线程,入线程中同时听声音,看图像,看弹幕,等等
3.Process和Thread
-
说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
-
而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
-
通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位
注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,及多核,入服务器。如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错局
4.核心概念
-
线程就是独立的执行路径
-
在程序运行时,即使自己没有创建线程,后台也会有多个线程,如,主线程,gc线程
-
main()称之为主线程,为系统的入口,用于执行整个程序
-
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统密切相关的,先后顺序是不能认为的干预的
-
对一份资源操作时,会存在资源的抢夺问题,需要加入并发控制
-
线程会到来额外的开销,如,CPU调度时间,并发控制开销
-
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
········
03.继承Thread类(重点)
1.三种创建方式
2.Thread
-
自定义线程类继承Thread类
-
重写run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
3.调用start()方法主线程和方法线程同时进行
4.调用run()方法先执行方法线程的再执行主线程的方法
5.总结:
注意,线程开启不一定立即执行,由CPU调度执行
04.网图下载
//练习THread,实现多线程同步下载图片
public class TestThread02 extends Thread{
private String url;//网络图片地址
private String name;//保存的文件名
public TestThread02(String url,String name){
this.url=url;
this.name=name;
}
//下载图片线程的执行体
05.实现Runnable接口(重点)
-
定义MyRunnable类实现Runnable接口
-
实现run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
//创建线程方式2,:实现Runnable接口,重写Run方法,执行线程需要丢入Runnable接口实现类,调用start()方法
public class TestThread3 implements Runnable{
小结:
-
继承Thread类
-
继承Thread类具备多线程能力
-
启动线程:子类对象.start()
-
不建议使用:避免OOP单继承局限性
-
-
实现Runnable接口
-
实现Runnable具有多线程能力
-
启动线程:传入目标对象+Thread对象.start()
-
推荐使用:避免单继承局限性,灵活方面,方便同一个对象被多个线程使用
-
06.初识并发问题
发现问题:多个线程操作同一资源的情况下,线程不安全,数据紊乱
//多个线程同时操作一个对象
//买火车票的例子
//发现问题:多个线程操作同一资源的情况下,线程不安全,数据紊乱
public class TestThread4 implements Runnable{
//票数
private int ticketNume = 10;
数据紊乱,同一张票被重复拿到
07.龟兔赛跑
1.案例:龟兔赛跑-race
-
首先来个赛道距离,然后要离终点越来越近
-
判断比赛是否结束
-
打印出胜利者
-
龟兔赛跑开始
-
故事中是乌龟赢的,兔子需要睡觉,所以我们来模拟兔子睡觉
-
终于兔子赢得了比赛
//模拟龟兔赛跑
public class Race implements Runnable{
//胜利者
private static String winner;
08.实现Callable接口(了解)
利用callable改造下载图片案例
-
实现Callable接口,需要返回值类型
-
重写call方法,需要抛出异常
-
创建目标对象
-
创建执行服务:ExecutorService ser = Executor.newFixedThreadPool(1);
-
提交执行:Future<Boolean> result1 = ser.submit(t1);
-
获取结果:Boolean r1 = result1.get();
-
关闭服务器:ser.shutdownNow();
09.静态代理模式
结婚案例
-
你:真实角色
-
婚庆公司:代理你,帮你处理结婚的事
-
结婚:实现都实现结婚接口即可
//静态代理模式总结:
//真实对象(目标对象)和代理对象都要实现同一个接口
//代理对象要代理真实角色
//好处:
//代理对象可以做很多真实对象做不了的事情
//真实对象可以专注做自己的事情
public class StaticProxy {
public static void main(String[] args) {
//1.开启线程
// lamda表达式()-> System.out.println("我爱你")
new Thread(()-> System.out.println("我爱你")).start();
//相当于WeddingCompany weddingCompany = new WeddingCompany(new You());
// weddingCompany.HappyMarry();
new WeddingCompany(new You()).HappyMarry();
//2.静态代理
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMarry();
//原来的方法
//You you = new You();你要结婚
//you..HappyMarry();
}
}
//功能接口
interface Marry{
//人间四大喜事
//久旱逢甘霖
//他乡遇故知
//洞房花烛夜
//金榜题名时
void HappyMarry();
}
//真实角色,帮助你结婚
class You implements Marry{
10.Lambda表达式
-
λ希腊字母表中排序第十一位的字母,英文名称为Lambda
-
避免匿名内部类定义过多
-
其实质属于函数式编程的概念
(params)->expression[表达式]
(params)->statement[语句]
(params)->{statements}
a->System.out.println("i like lamda-->"+a); -
new Thread(()->System.out.println("多线程学习.......")).start);
-
为什么要使用Lambda表达式?
-
避免内部类定义过多
-
可以让代码看起来更简洁
-
去掉了一堆没有意义的代码,只留下核心的逻辑
-
-
理解Functional Interface(函数式接口)是学习Java8 lambda表达式的关键所在
-
函数式接口的定义:
-
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
public interface Runnable{
public abstract void run();
} -
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
-
-
lambda表达式推导
//推导lambda表达式
public class TestLambda1 {
//3.静态内部类
static class Like2 implements ILike{