美文网首页
慕课网 - 深入浅出Java多线程

慕课网 - 深入浅出Java多线程

作者: Hey_Shaw | 来源:发表于2018-04-18 21:06 被阅读29次

    课程地址:深入浅出Java多线程
    课程讲师:Arthur

    进程

    • 程序(任务)的执行过程 (动态性-当双击运行)

    • 持有资源(共享内存,共享文件)和线程(进程是资源的载体,也是线程的载体,脱离进程去谈论线程没有意义;)

    • 线程是系统最小的执行单元

    • 同一进程中有多个线程

    • 线程共享进程的资源

    线程交互方式:互斥,同步。

    011.png
    /**
     * 军队线程
     * 模拟作战双方的行为
     */
    public class ArmyRunnable implements Runnable {
    
        //volatile保证了线程可以正确的读取其他线程写入的值
        //可见性 ref JMM, happens-before原则
        volatile boolean keepRunning = true;
    
        @Override
        public void run() {
            while(keepRunning){
                //发动5连击
                for(int i=0;i<5;i++){
                    System.out.println(Thread.currentThread().getName()+"进攻对方["+i+"]");
                    //让出了处理器时间,下次该谁进攻还不一定呢!
                    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()+"结束了战斗!");
        }
    }
    
    /**
     * 隋唐演义大戏舞台
     */
    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) {
                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) {
                e.printStackTrace();
            }
            
            /*
             * 历史大戏留给关键人物
             */
            mrCheng.start();
            
            //万众瞩目,所有线程等待程先生完成历史使命
            try {
                mrCheng.join();   // 所有线程会等待调用join()方法的这个线程执行完成后,再执行后续内容
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println("战争结束,人民安居乐业,程先生实现了积极的人生梦想,为人民作出了贡献!");
            System.out.println("谢谢观看隋唐演义,再见!");
            
        }
        
        public static void main(String[] args) {
            new Stage().start();
        }
    }
    

    如何正确的停止Java中的线程

    • 废弃stop()方法

    • 使用退出标志

    • interrupt()方法初衷并不是用于停止线程,isInterrupted() 和 interrupted()测试当前线程是否被中断

    public class WrongWayStopThread extends Thread{
    
        public static void main(String[] args){
            WrongWayStopThread thread = new WrongWayStopThread();
            System.out.println("Starting thread ...");
            thread.start() ;
    
            try{
                Threaf.sleep(3000) ;
            }catch(InterruptedException e){
                e.printStackTrace() ;
            }
    
            System.out.println("Interrupting thread ...");
            thread.interrupt() ;    // 并不能使程序停下来
    
            try{
                Threaf.sleep(3000) ;
            }catch(InterruptedException e){
                e.printStackTrace() ;
            }
    
            System.out.println("Stopping thread ...");
        }
    
        public void run(){
        //  while(true){
            while(!this.isInterrupted()){  // 线程停下,其实相当于退出旗标的方法
                System.out.println("Thread is running ...");
            /*  long time = System.currentTimeMillis() ;
                while((System.currentTimeMillis()  - time) < 1000){
                    // 减少屏幕输入的空循环
                }
            */
                // 换成等效代码,线程不能正常结束,且抛出异常。原因是:当现场调用某些方法进入阻塞状态,此时该线程再被调用interrupt()方法,会产生两个结果,第一是中断状态被清除(this.isInterrupted()),线程的isInterrupted()方法不能返回表示是否被中断的正确状态;第二是sleep()方法会收到一个InterruptedException异常
                Threaf.sleep(3000) ; 
            }
        }
    }
    

    推荐使用退出旗标的方式退出线程

    线程的交互

    /**
     * 宇宙的能量系统
     * 遵循能量守恒定律:
     * 能量不会凭空创生或消失,只会从一处转移到另一处
     */
    public class EnergySystem {
        
        //能量盒子,能量存贮的地方
         private final double[] energyBoxes;
         private final Object lockObj = new Object();  // 锁对象
         
         /**
          * 
          * @param n    能量盒子的数量
          * @param initialEnergy 每个能量盒子初始含有的能量值
          */
         public EnergySystem(int n, double initialEnergy){
             energyBoxes = new double[n];
             for (int i = 0; i < energyBoxes.length; i++)
                 energyBoxes[i] = initialEnergy;
         }
         
         /**
          * 能量的转移,从一个盒子到另一个盒子
          * @param from 能量源
          * @param to     能量终点 
          * @param amount 能量值
          */
         public void transfer(int from, int to, double amount){
             
             synchronized(lockObj){
                 
    //           if (energyBoxes[from] < amount)
    //               return;
                //while循环,保证条件不满足时任务都会被条件阻挡
                 //而不是继续竞争CPU资源
                 // Wait set
                 while (energyBoxes[from] < amount){
                     try {
                        //条件不满足, 将当前线程放入Wait Set
                        lockObj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                 }
                 
                 
                 System.out.print(Thread.currentThread().getName());
                 energyBoxes[from] -= amount;
                 System.out.printf("从%d转移%10.2f单位能量到%d", from, amount, to);
                 energyBoxes[to] += amount;
                 System.out.printf(" 能量总和:%10.2f%n", getTotalEnergies());
                //唤醒所有在lockObj对象上等待的线程
                 lockObj.notifyAll();
             }
             
         }
         
         /**
          * 获取能量世界的能量总和 
          */
         public double getTotalEnergies(){
             double sum = 0;
             for (double amount : energyBoxes)
                 sum += amount;
             return sum;
         }
         
         /**
          * 返回能量盒子的长度
          */
         public  int getBoxAmount(){
             return energyBoxes.length;
         }
         
    }
    
    public class EnergyTransferTask implements Runnable{
    
        //共享的能量世界
        private EnergySystem energySystem;
        //能量转移的源能量盒子下标
        private int fromBox;
        //单次能量转移最大单元
        private double maxAmount;
        //最大休眠时间(毫秒)
        private int DELAY = 10;
        
        public EnergyTransferTask(EnergySystem energySystem, int from, double max){
            this.energySystem = energySystem;
            this.fromBox = from;
            this.maxAmount = max;
        }
        
        public void run() {
            try{
                while (true){
                    int toBox = (int) (energySystem.getBoxAmount()* Math.random());
                    double amount = maxAmount * Math.random();
                    energySystem.transfer(fromBox, toBox, amount);
                    Thread.sleep((int) (DELAY * Math.random()));
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    
    }
    
    public class EnergySystemTest {
    
        //将要构建的能量世界中能量盒子数量
        public static final int BOX_AMOUNT = 100;
        //每个盒子初始能量
        public static final double INITIAL_ENERGY = 1000;
    
        public static void main(String[] args){
            EnergySystem eng = new EnergySystem(BOX_AMOUNT, INITIAL_ENERGY);
            for (int i = 0; i < BOX_AMOUNT; i++){
                EnergyTransferTask task = new EnergyTransferTask(eng, i, INITIAL_ENERGY);
                Thread t = new Thread(task,"TransferThread_"+i);
                t.start();
            }
        }
    
    }
    

    争用条件:当多个线程同时共享同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏(corrupted),这种现象称为争用条件。

    012.png

    线程交互:互斥与同步

    互斥:同一时间只能有一条线程对我们的关键数据或临界区进行操作。

    同步:线程之间的通信机制,如某一线程做了一件事,用某种方式告诉其他的线程做完了。

    同步实现:wait() / notify() / notifyAll()

    如何扩展Java并发的知识

    • Java Memory Mode

      • JMM描述了Java线程如何通过内存进行交互
      • happens-before原则
      • synchronized、volatile & final怎么实现这一原则
    • Lock & 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
      • 线程池ExecutorService
      • Callable & Future
      • BlockingQueue

    相关文章

      网友评论

          本文标题:慕课网 - 深入浅出Java多线程

          本文链接:https://www.haomeiwen.com/subject/jphkkftx.html