JavaSE入门级-第十八课-多线程

 

前言

计算机中所有具备功能性的代码整合我们称为程序,也可以说是软件,当这个软件在运行中我们称它是一个进程,一个进程会有很多组成部分,每一个组成部分就是一个线程,如果这个程序比较单一就只有一个部分,就是单线程程序(我们现在做的控制台就是单线程)

  线程是进程执行的基本单位,他们是一起运行的(宏观)

  比如王者荣耀

 

 

 

ü  人物控制,金币计算,地图场景,音效,语音,数据统计 这些都是同时运行,如果王者荣耀看成一个进程,那么这些都是它的线程,王者荣耀是一个多线程的程序

 

 

 

操作系统(知识补充)

l  Operating System 是管理计算机硬件 和 软件资源的程序(是使用电脑的第一程序)操作系统经理过了几代的演变

单道审批系统

独享所有资源 CPU等待人工操作 穿孔的纸带 或者 卡片 I/O 问题严重,计算机运算比较快,大部分时间都在等待用户输入任务(作业)

批处理系统

晶体管,一次性输入,磁带上存储,载入内存操作,解决I/O问题,但是有任务等待问题还有速度执行问题

多道批处理系统

集成电路诞生,一次性读入多个作业,在读取输入的等待时间中,处理其他作业,无交互,等待时间长

分时系统

将CPU的使用权分成若干个细小的时间单位如30ms,保证雨露均沾,多个作业同时执行,宏观上

实时系统

人工智能机器人,需要依赖程序启动以后,动态采集数据

并行与并发

l  并行是指两个或者多个事件在同一时刻发生

l  并发是指两个或者多个事件在宏观上同时发生 太快了 1秒内 都是一起运行的,单个CPU在某一瞬间只能处理一个任务(所以我们考虑了多个CPU)

java.lang.Thread 线程

我们使用一个案例来看看究竟什么是线程

ü  葫芦娃 大战 孙悟空

 

 

 

ü  客户端代码:

 

 

 

 

ü  运行效果:

 

 

 

ü  两个对象交替运行,运行效果在两个对象间切换时间片

 

Thread类的常用方法

Thread表示java中的线程,不需要额外导入包,每个线程都拥有优先权,调用了Runtime.exit() 退出线程,run()方法为线程的执行方法创建线程

ü  方法列表部分:

方法名

方法功能

Thread ()

创建一个线程对象

Thread (Runnable target)

创建一个线程对象

Thread(String name)

创建一个线程对象

Thread(Runnable target, String name)

创建一个线程对象

Thread(ThreadGroup group, Runnable target)

创建一个线程对象

getId()

返回线程的表示ID

getName()

返回线程名称

getPriority()

返回线程优先级

getState()

返回线程状态

join()

等待这个线程死亡

run()

可执行方法,Runable

start()

导致此线程开始执行;调用此线程的run方法

sleep(long millis)

线程休眠,等待唤醒

ü  注意:run() 方法是是运行方法,start() 是改变线程转态

java.lang.Runnable 接口

实现线程的第二种方式

ü  代码示例:

 

 

 

ü  这是一个函数式接口

ü  使用Runnable实现线程

 

 

 

 

ü  客户端调用:

 

ü  解析:run保证方法名是相同的,本质上还是需要Thread的start()方法

 

多线程经典案例

案例1:泡茶

ü  客人来家里泡茶,洗杯子线程,烧水线程同时开启

 

 

 

ü  客户端代码:

 

 

 

ü  运行效果

 

 

 

 

案例2:ATM取款

ü  ATM有多台机器,去ATM上面取款应该是并发运行

 

 

 

 

 

ü  客户端代码:

 

 

 

ü  解析:金额总量是固定的,ATM机有3个,3个同时运行不需要相互等待

 

练习:

l  实现多线程窗口售票,或多线程食堂打饭

Java中的多线程

l  java主攻互联网服务,在这个方向上的多线程有不太一样的地方,多线程可以分为两种方向展现:

ü  一遍泡茶一遍洗杯子,是两个事件,两个类,两个对象,同时运行

ü  在100米赛道上,是一个时间,一个类,八个对象,同时运行

ü  从编码的角度说,两个类容易理解,但是连个对象就比较困难,如果两个对象共同访问同一份静态数据,说着是类共享数据,就会出现 线程安全问题

ü  代码实例:

 

 

 

ü  这个钱,会出错

同步 与 异步

线程同步与异步指的是资源的占用问题

ü  线程同步 synchronization:如果多个线程同时需要访问资源,线程A访问资源时必须 等待 其他线程没有访问改资源,称为线程同步,或 线程安全

ü  线程异步 asynchronization:如果多个线程同时需要访问资源,线程A访问资源 无需等待 其他线程,称为线程异步,或 线程不安全

优缺点比较

l  线程同步,数据数据安全,但是线程之间相互等待,效率比较低,线程异步不需要等待效率高,但是有数据污染风险

 

synchronized

ü  方法同步:

 

 

 

ü  代码解析:synchronized 是一个关键字,加载方法中,表示方法同步,即 有线程进入到方法中,方法访问的变量数据安全,必须等待次线程结束之后才能被其他线程访问

 

 

 

l  代码块同步:

 

 

 

ü  当对象进入代码块中,同步锁一样的效果,数据安全

 

线程死锁问题:

两个线程相互等待对方释放资源,就是死锁

 

 

 

 

 

 

l  客户端调用:

 

 

 

ü  谁先放手谁是狗

 

形成死锁的前提条件

ü  互斥条件:系统对于资源的分配,一次只能给一个进程

ü  不可剥夺条件:资源获得之后,除非主动释放否则不能强行剥夺

ü  请求与保持条件:对自己持有的资源保持不放,并且提出新的请求

ü  循环等待条件:存在资源等待循环(闭环)

l  四个条件为死锁必要条件

避免死锁问题

l  死锁没得救,一定会发生,但是我们可以减少它发生的概率

ü  使用顺序锁

ü  使用限时锁

ü  死锁检测

ü  添加中间层

 

练习:

l  银行家算法

线程状态

l  线程处在不同的阶段能够定义出不同状态

l  如下图所示:

 

 

 

l  总结状态:

ü  new新建:线程被创建,new Thread();

ü  runnable就绪:调用了 start() 方法,在线程池线程中等待运行

ü  running运行:获得时间片,得到CPU权限

ü  blocked阻塞:失去时间片,暂停运行

  • 等待阻塞:wait()方法,让出时间片
  • 同步阻塞:需要访问的资源被 synchronized 锁了
  • 其他阻塞:调用sleep() 或者 join() 或者 请求IO

ü  dead死亡:线程正常结束,或被杀死

 

线程转态转换方法:

新建 → 就绪

l  调用 start() 方法,调run() 方法是没用滴,run方法是给jvm调用的

 

 

 

就绪 → 运行

l  自然获得时间片,或者提高线程优先级(插队),优先级分为 1-10越大越牛逼

 

 

 

运行 → 死亡

l  main()方法结束,run()方法结束,或者背调用命令 kill

 

运行 → 阻塞 → 运行

sleep()方法

l  线程执行sleep()方法,进入阻塞状态,但是不会释放资源锁

 

 

 

sleep 时间结束后,不是马上run,需要等待时间片

 

wait()方法

l  线程调用wait方法,进入阻塞状态,会释放锁,必须拥有锁,否则不能直接wait()

 

 

 

wait(),和 notify()必须在 同步代码块内

l  wait(时间)时间到了会自己抢锁执行

 

 

 

l  当发生以下事件会被唤醒:

-      被notify 或者 notifyAll

-      没有其他线程了

-      经过限定时间以后

 

yield() 方法

l  暂停当前的线程,不会释放锁,从新让线程回到就绪状态,等待时间片

 

 

 

 

join()方法

l  把指定线程加入到当前方法中,被加入线程执行结束后才会继续执行原线程

 

 

 

 

 

 

 

Callable 接口(补充)

l  Callable也是函数式接口,只有一个方法,实现方式和 Runnable类似,但是Callable能够在执行方法结束后,获得返回值

  代码示例:

 

 

 

 

 

 

ü  可以使用 FutureTak .get()方法 获得返回值

线程池 调度工具

  Java通过Executors提供四种线程池

-      SingleThreadExecutor 单线程

-      FixedThreadPool  控制并发数

-      ScheduledThreadPool 预定线程,时间固定

-      CachedThreadPool 缓存线程

Lock可重入锁

  使用关键字synchrognized,能够解决同步问题但是语义表达不清晰,java的锁对象

 

 

 

ThreadLocal

l  ThreadLocal解决并发问题,是线程的一个本地变量,为每个线程提供独立的副本使得线程之间不会相互影响

  代码示例:

 

 

 

生产者 与 消费者(补充)

l  生产者 与 消费者模式指的是一种数据提供机制,java中使用线程的等待和唤醒来实现这类问题

l  如图所示:

 

 

 

ü  生产者提供数据,到数据缓冲区,消费者从数据缓冲区取数据

ü  缓冲区存在数据时,允许消费者获取

ü  缓冲区数据空缺时通知生产者制造

 

l  java实现代码如下:

数据类型:

 

 

 

 

生产者:

 

 

 

消费者:

 

 

 

 

缓冲区:

 

 

 

 

客户端代码:

 

 

posted @ 2021-07-22 14:43  二娃千里眼  阅读(73)  评论(0)    收藏  举报