第八章_多线程-Day18-《Java学习知识库》

admin 2025-11-02 01:02:08 编程 来源:ZONE.CI 全球网 0 阅读模式
  • 1、昨日复习
  • 2、线程的生命周期
  • 3、线程的同步
  • 4、线程的通信
  • 5、jdk新增线程创建方式

    1、昨日复习

    1. 谈谈你对程序、进程、线程的理解

    2. 代码完成继承Thread的方式创建分线程,并遍历100以内的自然数

    3. 代码完成实现Runnable接口的方法创建分线程,并遍历100以内的自然数

    4. 对比两种创建方式

    5. 说说你对IDEA中Project和Module的理解workspace projectproject modulepackage packageQQ截图20220112130049.png

      2、线程的生命周期

      QQ截图20220112130421.pngQQ截图20220112130923.png

      3、线程的同步

      问题的提出:多个线程执行的不确定性引起执行结果的不稳定 多个线程对账本的共享,会造成操作的不完整性,会破坏数据。1、问题:买票过程中出现了,重票、错票———->出现了线程的安全问题。2、原因:当某个线程操作中,尚未完成时,其他线程参与进来,也操作车票。3、如何解决:当一个线程在操作票时,其他线程不能参与进来,直到线程a操作完,其他线程才可以开始操作票,即使线程a出现了阻塞,也不能改变。4、java中,通过同步机制,来解决线程的安全问题。


    方式一:同步代码块synchronized(同步监视器){需要被同步的代码}说明:1、操作共享数据的代码,即为需要被同步的代码。—>不能包含代码多了,也不能少了。 2、共享数据:多个线程共同操作的变量。比如:票的数量。 3、同步监视器:锁。任何类的对象都可以充当锁。该对象不能是NULL值。 要求:多个线程必须共用同一把锁 补充:在实现Runnable接口创建多线程的方式中,可以考虑使用this充当同步监视器。在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器


    方式二:同步方法如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的。1、同步方法任然涉及到同步监视器,只是不需要我们显示的声明。2、非静态的同步方法,同步监视器默认是:this静态的同步方法,同步监视器默认是:当前类本身。


    5、同步的方式,解决了线程的安全问题。——>好处。 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程,效率低。——>局限性


    1、死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁 2、说明:(1)出现死锁后,不会出现异常,不会出现报错,只是所有的线程都处于阻塞状态,无法继续(2)我们使用同步时,要避免出现死锁。解决方法 :专门的算法、原则 尽量减少同步资源的定义 尽量避免嵌套同步


    方式三:Lock锁——-jdk5.0新增QQ截图20220112211255.pngQQ截图20220112211402.png

    1. package com.atguigu.java3;
    2. /*
    3. 银行有一个账户。
    4. 有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打
    5. 印账户余额。
    6. */
    7. public class AccountTest {
    8. public static void main(String[] args) {
    9. Account acct = new Account(0);
    10. Customer c1 = new Customer(acct);
    11. Customer c2 = new Customer(acct);
    12. c1.setName("甲");
    13. c2.setName("乙");
    14. c1.start();
    15. c2.start();
    16. }
    17. }
    18. class Account {
    19. private double balance;
    20. public Account(double balance) {
    21. this.balance = balance;
    22. }
    23. public synchronized void depoist(double amt){
    24. if (amt>0){
    25. try {
    26. Thread.sleep(1000);
    27. } catch (InterruptedException e) {
    28. e.printStackTrace();
    29. }
    30. balance+=amt;
    31. System.out.println(Thread.currentThread().getName()+":存钱成功,余额为:"+this.balance);
    32. }
    33. }
    34. }
    35. class Customer extends Thread{
    36. private Account account;
    37. public Customer(Account account) {
    38. this.account = account;
    39. }
    40. @Override
    41. public void run() {
    42. for (int i = 0; i < 3; i++) {
    43. account.depoist(1000);
    44. }
    45. }
    46. }

    4、线程的通信

    QQ截图20220114141602.png涉及到的三个方法:wait():一旦执行该方法,当前线程就进入阻塞状态,并释放同步监视器。notify():一旦执行该方法,就会唤醒wait的一个线程,如果有多个wait的线程,就唤醒优先级高的notifyAll():一旦执行该方法,就会唤醒所有的被wait的线程。说明:1、三个方法必须使用在同步代码块或同步方法中。2、这三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出异常。3、这三个方法是定义在Object类中的


    面试题:sleep()和wait()的异同1、相同点:一旦执行方法,都可以使得当前线程进入阻塞状态2、不同点:(1)两个方法声明的位置不同:Thread类中声明sleep(),Object()类中声明wait()(2)调用的范围不同:sleep()可以在任何需要的场景下调用,wait()必须使用在同步代码块 或同步方法中。(3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep不会释放 锁,wait会释放锁。

    1. package com.atguigu.java4;
    2. /*
    3. 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处
    4. 取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图
    5. 生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通
    6. 知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如
    7. 果店中有产品了再通知消费者来取走产品。
    8. 分析:
    9. 1、是否是多线程?是
    10. 2、是否有共享数据?是
    11. 3、如何解决线程的安全?三种
    12. 4、是否涉及到线程的通信?是
    13. */
    14. public class ProductTest {
    15. public static void main(String[] args) {
    16. Clerk clerk= new Clerk();
    17. Productor p1 = new Productor(clerk);
    18. p1.setName("生产者1");
    19. Customer c1 = new Customer(clerk);
    20. c1.setName("消费者1");
    21. Customer c2 = new Customer(clerk);
    22. c2.setName("消费者2");
    23. p1.start();
    24. c1.start();
    25. c2.start();
    26. }
    27. }
    28. class Clerk {
    29. private int productCount=0;
    30. public synchronized void produceProduct() {//this
    31. if (productCount<20){
    32. productCount++;
    33. System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
    34. notify();
    35. }else {
    36. try {
    37. wait();
    38. } catch (InterruptedException e) {
    39. e.printStackTrace();
    40. }
    41. }
    42. }
    43. public synchronized void consumeProduct() {//this
    44. if (productCount>0){
    45. System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
    46. productCount--;
    47. notify();
    48. }else {
    49. try {
    50. wait();
    51. } catch (InterruptedException e) {
    52. e.printStackTrace();
    53. }
    54. }
    55. }
    56. }
    57. class Productor extends Thread {
    58. private Clerk clerk;
    59. public Productor(Clerk clerk) {
    60. this.clerk = clerk;
    61. }
    62. @Override
    63. public void run() {
    64. System.out.println(getName()+":开始生产产品。。。");
    65. while (true){
    66. try {
    67. sleep(10);
    68. } catch (InterruptedException e) {
    69. e.printStackTrace();
    70. }
    71. clerk.produceProduct();
    72. }
    73. }
    74. }
    75. class Customer extends Thread {
    76. private Clerk clerk;
    77. public Customer(Clerk clerk) {
    78. this.clerk = clerk;
    79. }
    80. @Override
    81. public void run() {
    82. System.out.println(getName()+":开始消费产品。。。");
    83. while (true){
    84. try {
    85. sleep(20);
    86. } catch (InterruptedException e) {
    87. e.printStackTrace();
    88. }
    89. clerk.consumeProduct();
    90. }
    91. }
    92. }

    5、jdk新增线程创建方式

    新增方式一:实现Callable接口与使用Runnable相比, Callable功能更强大些 相比run()方法,可以有返回值 方法可以抛出异常 支持泛型的返回值 需要借助FutureTask类,比如获取返回结果

    Future接口 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。 FutrueTask是Futrue接口的唯一的实现类 。FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

    1、创建一个实现Callable的实现类2、实现call方法,将此线程需要执行的操作声明在call中3、创建一个Callable接口实现类的对象4、将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象5、将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法。6、获取Callable中call方法的返回值,调用FutureTask的对象.get();方法。

    如何理解实现Callable接口的方法创建多线程比实现Runnable接口创建多线程方式强大?1、call()可以有返回值2、call()可以抛出异常,被外面的操作捕获,获取异常的信息3、call()是支持泛型的


    新增方式二:使用线程池背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。 好处: 提高响应速度(减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理 corePoolSize:核心池的大小 maximumPoolSize:最大线程数 keepAliveTime:线程没有任务时最多保持多长时间后会终止QQ截图20220114190014.png1、提供指定线程数量的线程池。2、执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象。3、关闭线程池。

    以太坊cppgolang区别 编程

    以太坊cppgolang区别

    以太坊是一种去中心化的开源平台,它采用智能合约技术,旨在构建和运行不受干扰的分布式应用程序。作为目前最受欢迎的区块链平台之一,以太坊提供了多种编程语言的支持,其
    progolang 编程

    progolang

    Go语言(Golang)是由Google开发的一门静态类型编程语言。作为一名专业的Golang开发者,我深知这门语言的优势和特点。在本文中,我将介绍Golang
    golangn个发送者 编程

    golangn个发送者

    Golang是一种开源的编程语言,由Google团队开发,旨在提高程序的并发性和简化软件开发过程。在Go语言中,有时需要向多个接收者发送信息。本文将介绍如何在G
    golang技能图谱 编程

    golang技能图谱

    从互联网行业的快速发展到人工智能技术的日益成熟,各种编程语言也应运而生。而在这众多的编程语言中,Golang(即Go)作为一门强大且高效的开发语言备受关注。Go
    评论:0   参与:  10