当前位置:   article > 正文

Java 多线程_java 多线程 火车票

java 多线程 火车票

目录

1.线程控制

1.Join

1.2 Yield

2.线程的同步

2.1问题的提出

2.2 例题:

模拟火车站售票程序,开启三个窗口售票

 2.3注意

2.4 同步方法

3.  同步机制中的锁

4.Lock(显式锁)

4.1是什么

4.2 synchronized 与 Lock 的对比

5. 定时器

6. 死锁

6.1是什么

7.线程通信

7.1概述

7.2使用方式


1.线程控制

1.Join

join : 合并线程 ,多干线程合并为一个线程

  1. package com;
  2. /**
  3. * jion : 合并线程 , 多个线程合并为一个线程
  4. *
  5. * @author 人间失格
  6. * @data 2021年10月29日上午11:23:32
  7. */
  8. public class Thread_01_Join {
  9. public static void main(String[] args) {
  10. //创建线程对象
  11. Thread t1 =new Thread(new Processer_01());
  12. Thread t2 =new Thread(new Processer_01());
  13. //线程设置名字
  14. t1.setName("我是t1");
  15. t2.setName("是我t2");
  16. //启动线程
  17. t1.start();
  18. t2.start();
  19. //执行到join的时候 ,因为时t1调用的,所以main之后的代码,必须等t1执行完之后才能执行
  20. try {
  21. t1.join();
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. for (int i = 0;i<5 ;i++){
  26. System.out.println(Thread.currentThread().getName()+" : " + i);
  27. }
  28. }
  29. }
  30. //接口线程
  31. class Processer_01 implements Runnable{
  32. //覆写run方法
  33. @Override
  34. public void run(){
  35. for(int i = 0 ; i<5 ; i++){
  36. //currentThread : 获取当前线程队象 getName : 获取当前线程的名字
  37. System.out.println(Thread.currentThread().getName()+" : "+ i);
  38. }
  39. }
  40. }

1.2 Yield

yield : 暂停当前正在执行的线程,并让其他线程执行
            1. yield是静态方法, 写在那个线程里,就让那个线程让位
            2. 给同等级的优先级让位 , 不同优先级不让位
            3. 只让出当前执行的时间片, 下次让不让不一定(也就是说只能保证让一次位)
  yield() 应该做的是让当前运行线程回到可运行状态 , 以允许有相同优先级的其他线程获得运行机会
   因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行.
但是,实际中无法保证yield()达到让步目的, 因为让步的线程还有可能呗线程调度程序再次选中.
 
  结论 : yield() 从未导致下属昵称转到等待/睡眠/阻塞状态.
  在大多数情况下,yield() 将导致线程从运行状态转到可运行状态,但有可能没有效果

  1. public class Thread_02_Yield {
  2. public static void main(String[] args) {
  3. Thread t1 = new Processor_02();
  4. t1.start();
  5. for(int i=0;i<10;i++){
  6. System.out.println(Thread.currentThread().getName()+" : "+i);
  7. }
  8. }
  9. }
  10. class Processor_02 extends Thread{
  11. @Override
  12. public void run() {
  13. for (int i = 0; i < 10; i++) {
  14. if (i % 2 == 0) {
  15. Thread.yield();
  16. }
  17. System.out.println(getName()+" : "+i);
  18. }
  19. }
  20. }

2.线程的同步

2.1问题的提出

多个线程执行的不确定性引起执行结果的不稳定

多个线程对账本的共享,会造成操作的不完整性,会破坏数据

线程同步 : 
  当多个线程同时操作同一个数据的时候,尤其是更改操作,为了保证数据的一致性和正确性,需要进行一定的保护
  
  所以线程同步是一种数据安全机制
  
  同步编译和异步编译
  
  同步编译 : 线程之间不是独立的,相互有影响,必须一个个执行
  异步编译 : 线程之间是独立的,相互吗,没有影响
  
  以下程序 因为同时操作了某个数据,导致结果不正确
1. 可以使用 synchronized修饰符解决
              使用方式 : public synchronized void m1(){} 使用synchronized的方法,
                               不能被多个线程同时执行
               比如 访问该对象中的某一个加锁的成员方法,那么该队昂中所有加锁的成员方法全部锁定,
                        其他线程都无法访问,只能拍到等待,等待该线程执行结束,交出锁


                    比如访问一个类的加锁的静态方法,那么该类中所有加锁的静态方法 全部锁定

 2. synchronized{} 语句块解决
                           synchronized(对象){ //这种方式是把该对象中所有加锁的
                                                                成员方法和代码块锁全部锁定
                                        同步代码;
                           }        
                           
                           synchronized(类名.class){ //这种方式是把该类中所有加锁的
                                                                        静态方法和代码块锁全部锁定
                                           同步代码;
                          } 

  1. package com;
  2. /**
  3. * 线程同步 :
  4. * 当多个线程同时操作同一个数据的时候,尤其是更改操作,为了保证数据的一致性和正确性,需要进行一定的保护
  5. *
  6. * 所以线程同步是一种数据安全机制
  7. *
  8. * 同步编译和异步编译
  9. *
  10. * 同步编译 : 线程之间不是独立的,相互有影响,必须一个个执行
  11. * 异步编译 : 线程之间是独立的,相互吗,没有影响
  12. *
  13. * 以下程序 因为同时操作了某个数据,导致结果不正确
  14. * 1. 可以使用 synchronized修饰符解决
  15. * 使用方式 : public synchronized void m1(){} 使用synchronized的方法,
  16. * 不能被多个线程同时执行
  17. *
  18. * 比如 访问该对象中的某一个加锁的成员方法,那么该队昂中所有加锁的成员方法全部锁定,
  19. * 其他线程都无法访问,只能拍到等待,等待该线程执行结束,交出锁
  20. *
  21. * 比如访问一个类的加锁的静态方法,那么该类中所有加锁的静态方法 全部锁定
  22. * 2. synchronized{} 语句块解决'
  23. * synchronized(对象){ //这种方式是把该对象中所有加锁的
  24. * 成员方法和代码块锁全部锁定
  25. * 同步代码;
  26. * }
  27. *
  28. * synchronized(类名.class){ //这种方式是把该类中所有加锁的
  29. * 静态方法和代码块锁全部锁定
  30. * 同步代码;
  31. * }
  32. *
  33. * @author 人间失格
  34. * @data 2021年10月29日下午6:31:29
  35. */
  36. public class Thread_03_Synchronization {
  37. public static void main(String[] args) {
  38. //创建A对象传入参数10 ,也就是i=10
  39. A a = new A(10);
  40. A a1 = new A(11);
  41. //创建线程对象 因为是接口类型所以用这样的创建对象的方法
  42. Thread t1 = new Thread(new Processor_03(a));
  43. Thread t2 = new Thread(new Processor_03(a));
  44. t1.setName("t1");
  45. t2.setName("t2");
  46. t1.start();
  47. t2.start();
  48. }
  49. }
  50. class Processor_03 implements Runnable {
  51. A a;
  52. public Processor_03(A a) {
  53. super();
  54. this.a = a;
  55. }
  56. @Override
  57. public void run() {
  58. a.m1();
  59. }
  60. }
  61. class A{
  62. int i ;
  63. //有参构造
  64. public A(int i) {
  65. super();
  66. this.i = i;
  67. }
  68. //方法锁
  69. // public synchronized void m1(){
  70. public synchronized void m1(){
  71. System.out.println("1--------------------1");
  72. try {
  73. //睡眠 会出现异常
  74. Thread.sleep(1000);
  75. } catch (InterruptedException e) {
  76. e.printStackTrace();
  77. }
  78. i++;
  79. System.out.println(Thread.currentThread().getName()+" : "+i);
  80. System.out.println("2@@@@@@@@@@@@2");
  81. }
  82. }

2.2 例题:

模拟火车站售票程序,开启三个窗口售票

  1. class Ticket implements Runnable {
  2. private int tick = 100;
  3. public void run() { while (true) {
  4. if (tick > 0) {
  5. System.out.println(Thread.currentThread ().getName() + "售出车票,tick号为:" + tick--);
  6. } else{
  7. break;
  8. }
  9. }
  10. }
  1. class TicketDemo {
  2. public static void main(String[] args) {
  3. Ticket t = new Ticket();
  4. Thread t1 = new Thread(t);
  5. Thread t2 = new Thread(t);
  6. Thread t3 = new Thread(t);
  7. t1.setName("t1窗口");
  8. t2.setName("t2窗口");
  9. t3.setName("t3窗口");
  10. t1.start();
  11. t2.start();
  12. t3.start();
  13. }
  14. }

理想状态:

 极端状态 :

  1. private int tick = 100;
  2. public void run(){
  3. while(true){
  4. if(tick>0){
  5. try{
  6. Thread.sleep(10);
  7. }catch(InterruptedException e){
  8. e.printStackTrace();
  9. }
  10. System.out.println(Thread.currentThread().getName()+“售出车票,tick号为:"+tick--);
  11. }
  12. }
  13. }

 2.3注意

  1. 多线程出现了安全问题
  2. 问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有 执行完,另一个线程参与进来执行。导致共享数据的错误。

     3.解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以 参与执行。

2.4 同步方法

Synchronized的使用方法

  1. Java对于多线程的安全问题提供了专业的解决方式:同步机制
  1. 同步代码块:

synchronized (对象){

// 需要被同步的代码;

}

  1. synchronized还可以放在方法声明中,表示整个方法为同步方法。

例如:

public synchronized void show (String name){

….

}

3.  同步机制中的锁

同步锁机制:

在《Thinking in Java》中,是这么说的:对于并发工作,你需要某种方式来防 止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法 就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须 锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁 之时,另一个任务就可以锁定并使用它了。

  1. synchronized的锁是什么?
  • 任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
  • 同步方法的锁:静态方法(类名.class)、非静态方法(this)
  • 同步代码块:自己指定,很多时候也是指定为this或类名.class
  1. 注意:
  • 必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就

无法保证共享资源的安全

  • 一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方 法共用同一把锁(this),同步代码块(指定需谨慎)

释放锁的操作

  1. 当前线程的同步方法、同步代码块执行结束。
  2. 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、 该方法的继续执行。
  3. 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
  4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线 程暂停,并释放锁。

不会释放锁的操作

  1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
  2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。

      应尽量避免使用suspend()和resume()来控制线程

4.Lock(显式锁)

4.1是什么

1.  从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。

2.  java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的 工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象 加锁,线程开始访问共享资源之前应先获得Lock对象。

3.   ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和 内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以 显式加锁、释放锁。

4.2 synchronized 与 Lock 的对比

  1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是 隐式锁,出了作用域自动释放
  2. Lock只有代码块锁,synchronized有代码块锁和方法锁
  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

优先使用顺序:

Lock  同步代码块(已经进入了方法体,分配了相应资源)同步方法(在方法体之外)

  1. class A{
  2. private final ReentrantLock lock = new ReenTrantLock(); public void m(){
  3. lock.lock();
  4. try{
  5. //保证线程安全的代码;
  6. }
  7. finally{
  8. lock.unlock();
  9. }
  10. }
  11. }
  12. 注意:如果同步代码有异常,要将unlock()写入finally语句块
  1. package com;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /**
  5. * JDK5.0 提出 : 代码块锁,性能较好
  6. * 又称位显示锁, 因为 开启和关闭 都是手动的
  7. * synchronized 是隐式锁,因为执行完全自动解锁
  8. *
  9. * @author 人间失格
  10. * @data 2021年10月29日下午6:57:13
  11. */
  12. public class Thread_04_Lock {
  13. public static void main(String[] args) {
  14. A1 a = new A1(10);
  15. Thread t1 = new Thread(new Processor_04(a));
  16. Thread t2 = new Thread(new Processor_04(a));
  17. t1.setName("t1");
  18. t2.setName("t2");
  19. t1.start();
  20. t2.start();
  21. }
  22. }
  23. class Processor_04 implements Runnable{
  24. A1 a ;
  25. public Processor_04(A1 a) {
  26. super();
  27. this.a = a;
  28. }
  29. @Override
  30. public void run() {
  31. a.m1();
  32. }
  33. }
  34. class A1{
  35. int i ;
  36. public A1(int i) {
  37. super();
  38. this.i = i;
  39. }
  40. //创建锁对象 Lock显式锁
  41. Lock lock = new ReentrantLock();
  42. public void m1(){
  43. System.out.println("@@@@@@");
  44. try {
  45. Thread.sleep(1000);
  46. } catch (InterruptedException e) {
  47. e.printStackTrace();
  48. }
  49. //开始加锁
  50. lock.lock();
  51. i++;
  52. System.out.println(Thread.currentThread().getName()+" : "+i);
  53. //解锁
  54. lock.unlock();
  55. System.out.println("==========");
  56. }
  57. }

5. 定时器

  1. package com;
  2. import java.text.ParseException;
  3. import java.text.SimpleDateFormat;
  4. import java.util.Date;
  5. import java.util.Timer;
  6. import java.util.TimerTask;
  7. /**
  8. * 定时器
  9. * @author 人间失格
  10. * @data 2021年10月29日下午7:11:01
  11. */
  12. public class Thread_05_Timer {
  13. public static void main(String[] args) throws ParseException {
  14. //创建定时器
  15. Timer timer = new Timer();
  16. //1. 做什么事
  17. //2. 开始时间 ,可以是时间(到了指定时间开始u执行), 也可以是毫秒数(当前时间开始,多长时间之后开始执行)
  18. //3. 执行的间隔时间
  19. //两秒之后开始执行,并且每隔1秒执行一次
  20. // timer.schedule(new LogTimerTask(), 2000, 1000);
  21. long m =System.currentTimeMillis();
  22. m+=1000*60;
  23. String string ="2021-10-29 19:30:00 000";
  24. Date d =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse(string);
  25. timer.schedule(new LogTimerTask(), d, 1000);
  26. }
  27. }
  28. //任务类
  29. class LogTimerTask extends TimerTask{
  30. @Override
  31. public void run() {
  32. System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()));
  33. }
  34. }

6. 死锁

6.1是什么

死锁 : 在执行过程中,都遇到了加锁的功能,从而进入等待状体,导致大家都访问不了
 
 1. 某个线程执行完成,需要 先后 嵌套 锁定 两个对象
 2. A线程 先进入第一个对象,并锁定第一个对象,在第一个对象中去嵌套访问并锁定第二个对象
 3. B线程,先进入第二个对象,并锁定第二个对象,在第二个对象中去嵌套访问并锁定第一个对象
 4. 当A线程把第一个对象锁定之后,要去访问第二个对象的时候
      发现已经被B线程锁住了,只能等待B线程交出第二个对象的锁才才能执行
 5. 当B线程把第二个对象锁定之后,要去访问第一个对象的时候, 
      发现已经被A线程锁住了,只能等待A线程交出第一个对象的锁才能执行
  6. 因此 导致A和B都进入等待状态

  1. public class Thread_06_DeadLock {
  2. public static void main(String[] args) {
  3. Object o1 = new Object();
  4. Object o2 = new Object();
  5. Thread t1 = new T1(o1,o2);
  6. Thread t2 = new T2(o1, o2);
  7. t1.start();
  8. t2.start();
  9. }
  10. }
  11. class T1 extends Thread{
  12. Object o1 ;
  13. Object o2 ;
  14. public T1(Object o1, Object o2) {
  15. super();
  16. this.o1 = o1;
  17. this.o2 = o2;
  18. }
  19. @Override
  20. public void run() {
  21. super.run();
  22. synchronized(o1){
  23. System.out.println("t1已经进入o1 准备进入o2");
  24. synchronized(o2){
  25. System.out.println("t1执行完成");
  26. }
  27. }
  28. }
  29. }
  30. class T2 extends Thread{
  31. Object o1 ;
  32. Object o2 ;
  33. public T2(Object o1, Object o2) {
  34. super();
  35. this.o1 = o1;
  36. this.o2 = o2;
  37. }
  38. @Override
  39. public void run() {
  40. super.run();
  41. System.out.println("@@@@@@@");
  42. synchronized(o2){
  43. System.out.println(" t2已经进入o2 准备进入o1");
  44. synchronized(o1){
  45. System.out.println("t2执行完成");
  46. }
  47. }
  48. }
  49. }

7.线程通信

7.1概述

包含: 

 

 

7.2使用方式

  1. package com;
  2. /**
  3. * 交叉执行
  4. *
  5. * wait : 让当前线程进入等待状态, 无参 或者传入 0 都意味着 不可以自动醒来,
  6. * 只能被唤醒,也可以传入毫秒数,到时间自动醒
  7. * notifyAll : 唤醒在当前对象中等待的所有线程
  8. * notify : 唤醒在当前对象中等待的某一个线程(不确定是那个)
  9. *
  10. * 以上两种方法必须用在成员加锁的方法中
  11. *
  12. * @author 人间失格
  13. * @data 2021年10月29日下午3:51:12
  14. */
  15. public class Thread_07_Wait {
  16. public static void main(String[] args) {
  17. //创建num对象
  18. Num num = new Num();
  19. //创建线程对象
  20. Thread t1 = new PrintOdd(num);
  21. Thread t2 = new PrintEven(num);
  22. //命名
  23. t1.setName("t1");
  24. t2.setName("t2");
  25. //启动
  26. t1.start();
  27. t2.start();
  28. }
  29. }
  30. class PrintOdd extends Thread{
  31. Num num;
  32. public PrintOdd(Num num) {
  33. super();
  34. this.num = num;
  35. }
  36. @Override
  37. public void run(){
  38. while(true){
  39. num.printOdd();
  40. }
  41. }
  42. }
  43. class PrintEven extends Thread{
  44. Num num;
  45. public PrintEven(Num num) {
  46. super();
  47. this.num = num;
  48. }
  49. @Override
  50. public void run(){
  51. while(true){
  52. num.printEven();
  53. }
  54. }
  55. }
  56. //业务类
  57. class Num{
  58. int count = 1;
  59. public synchronized void printOdd(){
  60. System.out.println(Thread.currentThread().getName()+" : "+count);
  61. count++;
  62. this.notifyAll();
  63. try {
  64. Thread.sleep(500);
  65. //挂起 交出该对象持有的锁 , 让其他线程可以执行
  66. this.wait();
  67. } catch (InterruptedException e) {
  68. e.printStackTrace();
  69. }
  70. }
  71. public synchronized void printEven(){
  72. System.out.println(Thread.currentThread().getName()+" : "+count);
  73. count++;
  74. this.notifyAll();
  75. try {
  76. Thread.sleep(500);
  77. //挂起 交出该对象持有的锁 , 让其他线程可以执行
  78. this.wait();
  79. } catch (InterruptedException e) {
  80. e.printStackTrace();
  81. }
  82. }
  83. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/632184
推荐阅读
相关标签
  

闽ICP备14008679号