• java基础多线程


      

    线程的创建

    方式1:继承Java.lang.Thread类,并覆盖run() 方法

    package com.demo.Thread;
    
    public class ThreadDemo01 extends Thread{
        public static void main(String[] args) {
            new ThreadDemo01().start();
        }
        @Override
        public void run() {
            int count = 50;
            while(count>0) {
                System.out.println(count--);
            }
        }    
    }
    继承Thread

    方式2:实现Java.lang.Runnable接口,并实现run() 方法

    package com.demo.Thread;
    
    public class ThreadDemo01 {
        public static void main(String[] args) {
            Thread th = new Thread(new Runnable() {
                public void run() {
                    int count = 50;
                    while(count>0) {
                        System.out.println(count--);
                    }
                }
            });
            
            th.start();
        }
    }
    实现run()接口

    线程的生命周期

    新生状态 (new)

    • new关键字建立一个线程对象后,该线程对象就处于新生状态。
    • 处于新生状态的线程有自己的内存空间,通过调用start进入就绪状态

    就绪状态(Runnable)

    • 处于就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU
    • 当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称之为“cpu调度”。

    运行状态(Running)

    • 在运行状态的线程执行自己的run方法中代码,直到等待某资源而阻塞或完成任务而死亡。
    • 如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。

    阻塞状态(Blocked)

    阻塞状态是线程因为某种原因放弃CPU使用权限,暂时停止运行。直到线程进入就绪状态,才有机会进入运行状态。阻塞的三种情况

    1. 等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。
    2. 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态。
    3. 其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入就绪状态。

    死亡状态:

    死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有三个

    1. 正常运行的线程完成了它的全部工作;
    2. 线程被强制性地终止,如通过执行stop方法来终止一个线程--容易导致死锁,不推荐。
    3. 线程抛出未捕获的异常

     

    Thread类常用方法

    String

    getName()

    返回该线程的名称。

    static void

    sleep(long millis)

    在指定的毫秒数内让线程休眠

    void

    start()  

    使该线程开始执行

    Void

    join()

    等待该线程终止。

    static void

    yield()

    暂停当前正在执行的线程对象,并执行其他线程

    static Thread

    currentThread()

    返回对当前正在执行的线程对象

    sleep()方法

    sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;
       sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。
      在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。

    wait()方法

    wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);其他线程可以访问;
      wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。
      wiat()必须放在synchronized block中,否则会在program runtime时扔出”java.lang.IllegalMonitorStateException“异常。

    package com.demo.reg;
    
    public class TestD {
    
        public static void main(String[] args) {
            new Thread(new Thread1()).start();
            try {
                Thread.sleep(5000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            new Thread(new Thread2()).start();
        }
        
        private static class Thread1 implements Runnable{
            @Override
            public void run(){
                synchronized (TestD.class) {
                System.out.println("enter thread1...");    
                System.out.println("thread1 is waiting...");
                try {
                    //调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池
                    //只有当它被notify()时候,且对象锁没有被持有就能够继续运行下去
                    TestD.class.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("thread1 is going on ....");
                System.out.println("thread1 is over!!!");
                }
            }
        }
        
        private static class Thread2 implements Runnable{
            @Override
            public void run(){
                synchronized (TestD.class) {
                    System.out.println("enter thread2....");
                    System.out.println("thread2 is sleep....");
                    //只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
                    TestD.class.notify();
                    try {
                        //在调用sleep()方法的过程中,线程不会释放对象锁,
                        Thread.sleep(5000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread2 is going on....");
                    System.out.println("thread2 is over!!!");
                }
            }
        }
    //    控制台结果
    //    enter thread1...
    //    thread1 is waiting...
    //    enter thread2....
    //    thread2 is sleep....
    //    thread2 is going on....
    //    thread2 is over!!!
    //    thread1 is going on ....
    //    thread1 is over!!!
        
        
    }

    线程礼让Yield

    static void yield ()

    让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态

    如果调用了yield方法之后,没有其他等待执行的线程,这个时候当前线程就会马上恢复执行!

     

    package com.demo.Thread;
    
    public class ThreadDemo01 {
        public static void main(String[] args) {
            new Thread(new YieldDemo()).start();
            
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i = 1;i <= 50;i++) {
                        System.out.println("Thread:"+i);
                    }
                }
            }).start();
            
        }
    }
    
    class YieldDemo implements Runnable{
        public void run() {    
            for(int i = 1;i <= 50;i++) {
                System.out.println("YieldDemo:"+i);
                if(i%5==0) {
                    System.out.println("###########礼让###########");
                    Thread.yield();
                }
                
            }
        }
    }

    线程插队Join

    void join() 

    合并线程,等待此线程执行完毕后,在执行其他线程,其他线程进入阻塞状态

    为什么要用join()方法

    在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了

    观察线程状态变化

    public Thread.State  getState()

    返回该线程的状态。 该方法用于监视系统状态,不用于同步控制。

    枚举 Thread.State

    线程状态。线程可以处于下列状态之一:

    NEW

      尚未启动的线程处于这种状态

    RUNNABLE

      可运行线程的线程状态,就绪状态和运行状态

    BLOCKED

      受阻塞并等待某个监视器锁的线程处于这种状态

    WAITING

      无限期地等待另一个线程来执行某一特定操作的线程处于这种状态,某一线程因为调用下列方法之一而处于等待状态。

    •   有时间的wait
    •   有时间的.join

    TIMED_WAITING

      等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态,某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:

    •      sleep
    •   有时间的 wait
    •   有时间的join

    TERMINATED

      线程结束死亡

    package com.demo.Thread;
    
    public class ThreadDemo01 {
        public static void main(String[] args) {
            
             Thread t = new Thread(()-> {
                 // 每500毫秒阻塞一次
                 for(int i = 0;i < 5;i++) {
                     try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                 }
                 
             });
            
             // 新生状态
             System.out.println("状态:"+t.getState());
             
             // 启动线程
             t.start();
             
            // 每500毫秒查看线程的状态
             for(int i = 0;i < 8;i++) {
                try {
                     System.out.println("状态:"+t.getState());
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             }
        /*     
             控制台输出
             状态:NEW
             状态:RUNNABLE
             状态:TIMED_WAITING
             状态:TIMED_WAITING
             状态:TIMED_WAITING
             状态:TIMED_WAITING
             状态:RUNNABLE
             状态:TERMINATED
             状态:TERMINATED
        */
        }
    }

    线程优先级

    Thread类 提供三种优先级常量

    线程优先级1-10,

    MAX_PRIORITY 10

    MIN_PRIORITY 1

    NORM_PRIORITY 5

    优先级越大代表被cpu优先执行的概率越大

    package com.demo.Thread;
    
    public class ThreadDemo01 {
        public static void main(String[] args) {
            // 设置线程的名字
            Thread old = new Thread(new MyThread(),"老人");
            Thread child = new Thread(new MyThread(),"小孩");
            Thread woman = new Thread(new MyThread(),"女人");
            Thread man = new Thread(new MyThread(),"男人");
            // 设置优先级(必须在启动线程之前设置优先级)
            old.setPriority(Thread.MAX_PRIORITY);
            child.setPriority(Thread.MAX_PRIORITY);
            woman.setPriority(Thread.MIN_PRIORITY);
            man.setPriority(Thread.MIN_PRIORITY);
            // 启动线程
            man.start();
            woman.start();
            child.start();
            old.start();
            
    //        old.start();
    //        child.start();
    //        woman.start();
    //        man.start();
        }
    }
    
    class MyThread implements Runnable{
        public void run() {
            // 输出当前线程的名字和优先级
            System.out.println(Thread.currentThread().getName()+"------>"+Thread.currentThread().getPriority());
        }
    }
    测试线程优先级

     守护线程

    守护线程是给其他程提供服务用的,比如gc就是守护线程。

    那么如果所有的非守护线程都执行完毕后,那么守护线程和JVM就会一起结束(因为没有可以守护的线程,当然守护线程就结束)

    主线程结束后,普通线程继续执行。

    package com.demo.Thread;
    
    public class ThreadDemo01 {
        public static void main(String[] args) {
            Thread t = new Thread(()-> {
                // 无限循环
                while(true) {
                    try {
                        System.out.println("我是普通线程");
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                }
            });
    
            t.start();
            
            for(int i = 1;i <= 5;i++) {
                try {
                    System.out.println("main线程 time="+i);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
    //        控制台结果
    //        main线程 time=1
    //        我是普通线程
    //        main线程 time=2
    //        我是普通线程
    //        我是普通线程
    //        main线程 time=3
    //        main线程 time=4
    //        我是普通线程
    //        我是普通线程
    //        main线程 time=5
    //        我是普通线程
    //        我是普通线程
    //        我是普通线程
    //        我是普通线程
    //        ...
    //        ... 
    //        ... 
    //        ... 一直输出下去
        }
    }
    普通线程

    设置为守护线程的时候,非守护线程全部结束后,守护线程和jVM一并结束

    package com.demo.Thread;
    
    public class ThreadDemo01 {
        public static void main(String[] args) {
            Thread t = new Thread(()-> {
                // 无限循环
                while(true) {
                    try {
                        System.out.println("我是普通线程");
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                }
            });
            // 设置为守护线程,如果非主线程全部结束后,守护线程将会结束
            t.setDaemon(true);
            t.start();
            
            for(int i = 1;i <= 5;i++) {
                try {
                    System.out.println("main线程 time="+i);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
    //        控制台打印
    //        main线程 time=1
    //        我是普通线程
    //        我是普通线程
    //        main线程 time=2
    //        我是普通线程
    //        main线程 time=3
    //        我是普通线程
    //        main线程 time=4
    //        我是普通线程
    //        main线程 time=5
    //        我是普通线程
    
        }
    }
    守护线程

    synchronized

    synchronized关键字的作用域有二种:

    1. 是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
    2. 是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。 

    除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象; 

    synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;

    A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。

    B.每个对象只有一个锁(lock)与之相关联。

    任务调度

    Timer

    一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。

    构造方法

    Timer(String name, boolean isDaemon)

              创建一个新计时器,其相关的线程具有指定的名称,并且可以指定作为守护程序运行

    方法

    void schedule(TimerTask task, Date time)

              安排在指定的时间执行指定的任务

    void schedule(TimerTask task, Date firstTime, long period)

              安排指定的任务在指定的时间开始进行重复的固定延迟执行

    void schedule(TimerTask task, long delay)

              安排在指定延迟后执行指定的任务

    void schedule(TimerTask task, long delay, long period)

              安排指定的任务从指定的延迟后开始进行重复的固定延迟执行

    TimerTask

    由 Timer 安排为一次执行或重复执行的任务。

    方法

    abstract  void run()

              此计时器任务要执行的操作

    参考

    https://www.cnblogs.com/GarfieldEr007/p/5746362.html

  • 相关阅读:
    json学习笔记
    尾调用学习
    t分布, 卡方x分布,F分布
    第三章 概率 与 概率分布
    二项分布&超几何分布
    第二章 试验资料的整理与特征数的计算
    第一章 常用统计学术语
    python 网络编程 TCP/IP socket UDP
    Python3 标准库概览
    python 输入输出,file, os模块
  • 原文地址:https://www.cnblogs.com/rainouyang/p/10807113.html
Copyright © 2020-2023  润新知