zoukankan      html  css  js  c++  java
  • 深入浅出Java多线程

    进程与线程

    进程:程序(任务)的执行过程(动态的),持有资源(共享内存,共享文件)和线程

    如在我们电脑上的QQ,如果只是放在那里,并不是进程,只有当你点击它运行后,才启动了一个进程。

    线程:如QQ,可以文字聊天,同时首发文件,这就是2个线程。

    线程是系统中最小的执行单元,同一个进程可以拥有多个线程,线程共享进程的资源。

    线程的交互:互斥(竞争)与同步(合作)

    如,在同一个班级中,每个同学都是一个线程,大家共享公共资源如桌椅,黑板,学习资料等。同学间可以交流,关系有互斥与同步。如当同学A与同学B同时向使用某个学习资料时,就产生了互斥,需要一个一个使用;当学校组织文艺汇演时,需要同学间互相合作,才能表演优秀的节目。

     Java线程之初体验

    • Java对线程的支持
      • Thread类与Runnable接口,都有run()方法
    • 线程的创建和启动
    • 线程常用方法
    •  

    以隋唐英雄演绎这个实战项目来进一步了解Java的多线程

    • 根据故事梗概,我们需要“军队-ArmyRunnable”,“英雄人物-KeyPersonThread”,“舞台-Stage”这三个类;
    • /**
       * 军队线程
       * 模拟作战双方的行为
       * @author
       *
       */
      public class ArmyRunnable implements Runnable {
          //volatile(可见性 JMM, happens-before原则)保证线程可以正确读取其他线程写入的值
          volatile boolean keepRunning = true;
      
          @Override
          public void run() {
              // TODO Auto-generated method stub
              while(keepRunning) {
                  //发动5连击
                  for(int i=0; i<5; i++) {
                      System.out.println(Thread.currentThread().getName() + 
                              "进攻对方第[" + i + "]击");
                      //让出了CPU时间,下次该谁进攻还不一定呢!
                      Thread.yield();
                  }
              }
              System.out.println(Thread.currentThread().getName() + "结束了战斗!");
          }
      
      }
      
      
      public class KeyPersonThread extends Thread {
          public void run() {
              System.out.println(Thread.currentThread().getName() + "开始了战斗!");
              
              for(int i=0; i<10; i++) {
                  System.out.println(Thread.currentThread().getName() + "左突右杀,攻击隋军。。。");
              }
              
              System.out.println(Thread.currentThread().getName() + "结束了战斗!");
          }
      }
      
      
      /**
       * 隋唐演义的大戏舞台
       * @author
       *
       */
      public class Stage extends Thread {
          public void run() {
              
              System.out.println("欢迎观看隋唐演义");
              try {
                  Thread.sleep(5000);
              } catch (InterruptedException e1) {
                  e1.printStackTrace();
              }
              System.out.println("大幕徐徐打开");
              
              try {
                  Thread.sleep(5000);
              } catch (InterruptedException e1) {
                  // TODO Auto-generated catch block
                  e1.printStackTrace();
              }
              
              System.out.println("话说隋朝末年,隋军与农民起义军杀得昏天黑地...");
              
              
              ArmyRunnable armyTaskOfSuiDynasty = new ArmyRunnable();
              ArmyRunnable armyTaskOfRevolt = new ArmyRunnable();
              
              //使用Runnable接口启动线程
              Thread armyOfSuiDynasty = new Thread(armyTaskOfSuiDynasty, "隋军");
              Thread armyOfRevolt = new Thread(armyTaskOfRevolt, "起义军");
              
              //启动线程,让双方军队开始作战
              armyOfSuiDynasty.start();
              armyOfRevolt.start();
              
              //舞台线程休眠,大家专心观看军队的厮杀
              try {
                  Thread.sleep(50);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              
              
              System.out.println("正当双方激战正酣,半路杀出了个程咬金");
              Thread mrCheng = new KeyPersonThread();
              mrCheng.setName("程咬金");
              System.out.println("程咬金的理想就是结束战争,使百姓可以安居乐业!");
              
              //军队停止作战,停止线程的方法
              armyTaskOfSuiDynasty.keepRunning = false;
              armyTaskOfRevolt.keepRunning = false;
              
              try {
                  Thread.sleep(2000);
              } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
              
              //历史大戏留给关键人物
              mrCheng.start();
              try {
                  mrCheng.join();
              } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
              
              System.out.println("战争结束,人民安居乐业,程先生实现了积极的人生梦想,为人民作出了贡献");
              System.out.println("谢谢观看隋唐演义,谢谢!");
              
          }
          
          public static void main(String[] args) {
              // TODO Auto-generated method stub
              new Stage().start();
          }
      
      }
      View Code
    • 如何正确的停止线程
      • stop()方法就是一个不正确的停止线程的方法;它会使线程戛然而止,我们不知道它完成了什么工作,没有完成什么工作,因此是错误的。
      • 使用退出旗标:如上面的例子中keepRunning就是一个结束标志。
      • interrupt()方法:初衷并不是用于停止线程

       Java线程交互

    • 消失的能量
      • 什么是争用条件(Race Condition)
        • 当多个线程同时访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据破坏(corrupted),这种现象称为争用条件。
        • 如上图所示,当某个时间片时,线程1获得了CPU时间,从主存中读取了energyBox[to]的值,然后加上500,但还没有写回内存时,CPU时间耗尽;然后线程2抢到了CPU,读取的energyBox[to]的值仍为5000,然后它操作加上900,并写回内存5900,然后CPU时间耗尽。而下面又是线程1获得了时间片,将它上面计算完的5500写入内存,导致最终内存中存储的值为5500.而通过线程1和线程2的修改,最终写入内存的energyBox[to]的值应该为6400才是正确的!经过这样的方式运行,就发生了能量不守恒的结果。
        • 那么,如何修改代码,使能量变得守恒呢?
          • 互斥与同步,得到线程正确的进行交互。
          • 互斥:关键数据在同一时间只能被一个线程访问。实现:synchronized(intrinsic lock)
          • 同步:由于资源某些条件不满足,导致所有的线程陷入等待状态;而当访问资源的条件满足后,会唤醒所有的线程,进入互斥状态。实现:wait()/notify()/notifyAll()——都是Object对象的方法。
          • wait()/notify()/notifyAll()不是在同一个线程的同一次操作中执行的。因为同步是指多个线程之间的同步,只有一个线程的话是不需要同步的!!
          • 当一个线程要访问一个临界区(Critical Section)时,首先要获得该临界区的锁,当获得了锁后去判断是否满足获得该资源的条件,当条件不满足时,将释放锁(使得其他线程可以进入临界区),并进入该锁对象上的等待(wait())队列中等待。当其它线程使用完了临界区后,将告知(notifyAll())所有等待队列中的线程:目前临界区内的条件有变化,大家可以开始竞争锁对象。

        要点回顾

    • 如何创建线程及线程的基本操作;
    • 可见性及volatile关键字;
    • 争用条件;
    • 线程的互斥synchronized;
    • 线程的同步wait/notifyAll();

      扩展

    • Java Memory Mode
      • JMM描述了Java线程如何通过内存进行交互
      • happens-before原则
      • synchronized, volatile & final
    • Locks & Condition
      • Java锁机制和等待条件的高层实现
      • java.util.concurrent.locks
    • 线程的安全性
      • 原子性与可见性
      • java.util.concurrent.atomic
      • synchronized & volatile
      • DeadLocks
    • 多线程常用的交互模型
      • Producer-Consumer模型
      • Read-Write Lock模型
      • Future模型
      • Worker Thread模型
    • Java5中并发编程工具
      • java.util.concurrent
      • 线程池ExcecutorService
      • Callable & Future
      • BlockingQueue
  • 相关阅读:
    团队作业6——复审与事后分析
    Alpha阶段项目复审
    测试与发布( Alpha版本 )
    测试与发布(Alpha版本)
    团队作业4——项目冲刺
    第 1 篇 Scrum 冲刺博客
    第 7篇 Scrum 冲刺博客
    第 6篇 Scrum 冲刺博客
    第 5篇 Scrum 冲刺博客
    第 4篇 Scrum 冲刺博客
  • 原文地址:https://www.cnblogs.com/little-YTMM/p/5404126.html
Copyright © 2011-2022 走看看