7:Multithreading-Java API 实战

时间:2022-12-07 10:57:14

(目录)

1. 问题的提出

一台计算机为何能够执行多个程序?它们是怎么执行多个程序的?

  • 电脑可以同时做很多事情,一边聊天,一边听歌,一边上网查资料等
  • 原因是电脑有多个核心(脑子),一个核心可以做一件事情,多个核心就可以做多件事情
  • 而多线程就是一台电脑,CPU可以同时运行两个程序(表面上),实际上是进程切换的快,第一个进程打开,第二个进程挂起,给你一种错觉

7:Multithreading-Java API 实战 例如CPU的6核12线程,就相当于有6个工人去运行进程

7:Multithreading-Java API 实战

2. 核心数、进程、线程

  • 一个核心下有多个进程,而一个进程下又会有多个线程

3. 进程和线程的区别以及对应应用

进程和线程的区别

  • 线程只是一个进程中不同执行的路径
  • 进程与进程之间不会相互影响,因为它们是占有独立内存的
  • 而线程是占用共同的内存,所以一个线程出问题,那这个进程下的线程都会出问题
  • 多进程的程序要比多线程的程序健壮,而多线程运行效率更高,但是线程不能独立执行,必须依存在应用程序中,操作系统不会把线程看作多个独立应用
  • 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。例如很多人共同抢一双鞋,就要用到多线程

并发与并行

  • 并发(Concurrent)指一个CPU需要进行多个进程,这样就需要不停的切换,让进程不断的交替执行
  • 并行(Parallel)指多个CPU同时执行多个进程

4. 多线程程序含义、多线程的作用

创建多线程DemoThread类,Alt+Insert调出Generate选择Override Methods

7:Multithreading-Java API 实战 选择run():void 7:Multithreading-Java API 实战mainThreadDemoThread两个字符串交替执行

7:Multithreading-Java API 实战 如果用.run()的话会出现问题,死循环一直在跑,用.start()可以多开启一个线程,然后去自动调用.run(),再继续进行当前线程 7:Multithreading-Java API 实战

显示的结果是两个死循环的内容在交替执行,其原因就是使用了.start()后两个线程一直在执行

5. 多线程的执行过程

多线程的执行方式

  • 一般的程序是从main出发,直线向下进行,只有一条主线
  • 多线程在main主线程序遇到线程程序时会转到线程程序,并返回到主线程序中,这样main程序和线程程序同时执行

7:Multithreading-Java API 实战

6. Runnable

前面我们创建线程单继承了Thread,无法继承别的类,因为Java不支持多继承

使用Runable接口来创建线程,要使用其方法,必须创建Thread对象实现

7:Multithreading-Java API 实战

Ctrl + 左键查看Thread源码 7:Multithreading-Java API 实战

源码中,Thread类就是实现了Runnable接口,而在Thread的构造方法中也有许多方法函数需要传递Runnable接口类型

7:Multithreading-Java API 实战

而我们在主函数中实现的应该是第一种传递方式,Thread的构造方法还有很多方式,这些构造函数都有一个特点就是全部使用init这个方法对线程进行实现

init源码的变量注释及源码

  • 在原始的init中名字是不能为空的,如果名字为空会报空指针异常,但是在其他的函数中,如果程序员不给thread.name赋值的话也可以自动生成一些值
  • 涉及线程组相关的安全问题
  • 变量的赋值和其他函数的初始化相关

7:Multithreading-Java API 实战7:Multithreading-Java API 实战

下面init方法是对上面的函数的衍生,是构造方法中使用的初始方法 7:Multithreading-Java API 实战

7. 简化操作以及线程名

Thread源码里面有传线程名的构造方法,要在原来线程类中自动获取我们在主线程中设置的名字,使用Thread.currentThread().getName()方法,.currentThread()是指当前线程,.getName()是指获取名字

7:Multithreading-Java API 实战

8. 抢购鞋——多线程案例

两种创建线程的方式,一种使用继承,一种使用接口实现,解决了线程名的问题,接下来我们模拟一个多线程的抢鞋程序

抢鞋的逻辑代码涵盖在线程当中,假设有10双鞋,有三个人来抢,一个线程就是一个用户,所以这就有三个名称不一样的线程名

使用Runnable接口创建

7:Multithreading-Java API 实战

9. 后台、守护进程的提出

电脑任务管理器

  • Apps是前台进程,Background processes是后台进程也叫守护进程,这些进程在电脑开机时就被启动,这样电脑才能正常且安全的运作起来,在程序中也是同理
  • 与进程同理,前台线程为用户提供服务,也有后台线程为前台线程提供的服务进行保护或者守护

7:Multithreading-Java API 实战

后台线程的创建过程

  1. 创建一个DaemonThread,实现Runnable接口

  2. 重写run()方法

  3. 在运行类中创建先一个DaemonThread,再用 Thread 用来实现DaemonThread

  4. 最后调用setDaemon(true) 设置成后台守护线程,.start()开启线程 7:Multithreading-Java API 实战

10. 匿名内部类创建多线程——你们老师喜欢的

Runnable接口进行匿名处理

7:Multithreading-Java API 实战

11. 发现问题,提出synchronized的概念和用途

现实情况中,抢鞋肯定是有延时操作的,如果我们用.sleep()设置每次抢鞋之间的间隙,会产生了一个问题,就是线程不同步导致线程不安全,两个人同时抢了第7双鞋

7:Multithreading-Java API 实战

解决这个问题要用到线程同步,及时更新数据,即创建一个synchronized锁对象,同步数据 7:Multithreading-Java API 实战

12. synchronized同步方法

如何理解锁呢?当用户一抢到第一双鞋时,锁住第一双鞋,其它用户就无法抢了 7:Multithreading-Java API 实战

synchronized可以创建成一个同步方法,将同步代码块抽离出来 7:Multithreading-Java API 实战

13 Lock、ReentrantLock同步锁

synchronized与reentrantLock区别

  • synchronized 不需要用户去手动释放锁,代码执行完后系统会自动让线程释放对锁的占用
  • reentrantLock则需要用户去手动释放锁,如果没有手动释放锁,就可能导致死锁现象

7:Multithreading-Java API 实战

14. Unlock遗留问题,释放锁

释放reentrantLock锁,try catch要放在if外面,最后finally调用reentrantLock.unlock()方法

7:Multithreading-Java API 实战

15. 浅谈synchroized和Lock的区别

  • JDK1.5中,synchroized是重量级操作,性能低效,Lock性能高,更稳定
  • JDK1.6中,synchroized加入很多优化,更加稳定了

锁的释放

  • synchronized以获取锁的线程执行完同步代码,如果线程执行发生异常,jvm会让线程释放锁

  • Lockfinally中必须释放锁,不然容易造成线程死锁

死锁产生

  • synchronized在发生异常时候会自动释放占有的锁,不会出现死锁
  • Lock发生异常时候,不会主动释放,必须手动unlock来释放锁,可能引起死锁的发生

用法

  • synchronized在需要同步的对象中加入,可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象

  • Lock一般使用ReentrantLock类做为锁,通过lock()加锁和unlock()解锁指出,在finally中写unlock()防止死锁

16. Thread API说明

参考:Thread (Java Platform SE 7 ) - Oracle

17. CPU线程调度、Priority线程优先级、优先级常量、剩余小问题

CPU线程调度

  • 每一个线程的优先使用权都是系统随机分配的,人人平等,谁先分配到谁先用
  • 可以设置优先级赋予某一个线程拥有至高适用权,最高为10,最低为1,默认为5,Java可以抢占CPU 7:Multithreading-Java API 实战

线程1-10中,main()主线程的value = 5,创建 MaxPriorityThread 类和MinPriorityThread来查看线程执行顺序

7:Multithreading-Java API 实战.start()前面加优先级.setPriority()方法即可越权 7:Multithreading-Java API 实战

但有时会发现优先级没有调换过来,是操作系统的原因,程序执行太快了没有反应过来,还没调度程序就结束了 7:Multithreading-Java API 实战

18. join线程插队

.join()方法可以抢占优先级,实现插队 7:Multithreading-Java API 实战

19. sleep线程休眠

还是上一个例子,使用.sleep()方法休眠后,thread_1线程插队时,会等待1000毫秒再打印出结果 7:Multithreading-Java API 实战

20. yield线程让步

.yield()方法可以实现线程让步,让其它线程执行,thread_1输出一次的时候给thread_2让步了,有时程序运行的太快了,以至于还没打印出让步输出,thread_2已经输出完毕了

7:Multithreading-Java API 实战

21. 线程状态?嗯,还是来玩一盘游戏吧!

Java中线程的状态分为6种--以斗地主为例

1.新建(NEW)-新建一局游戏

2.可运行(RUNNABLE)-初始状态是可运行的

3.阻塞(BLOCKED)-谁出牌谁获得一个锁,导致阻塞,出好牌则疏通阻塞

4.等待(WAITING)-不出牌的等待通知

5.计时等待(TIMED_WAITING)-出牌时,其他人计时等待超时或通知

6.终止(TERMINATED)-游戏结束

7:Multithreading-Java API 实战

参考: Java线程的6种状态及切换(透彻讲解)

22. 发现实际问题,抛出线程通信的含义

线程优先级

  • Win10任务管理器中,线程有6个优先级设置
  • 线程的调度目的就是通知另一个线程去执行,也有其它办法去通知 7:Multithreading-Java API 实战

23. 线程的通信:wait和notify

线程通信,即等待唤醒机制

  • 最简单的例子如Producer生产者与Customer消费者和Condom产品的关系
  • 当产品Condom产品生产出来之后,消费者购买完,需要联系Producer厂商继续生产
  • .notify() 方法用于唤醒一个在此对象监视器上等待的线程
  • 一个线程在对象监视器上等待可以调用 .wait() 方法

7:Multithreading-Java API 实战

7:Multithreading-Java API 实战

24. notifyAll

.notifyAll()方法用于唤醒在该对象上等待的所有线程

25. 提及Process进程。点到为止,章节结束语和建议。

多线程掌握基础,当学习到框架时,需要深入并发编程

参考:操作进程拓展Class ProcessBuilder