zoukankan      html  css  js  c++  java
  • Java并发-多线程基础

    线程的定义

    一个程序就是一个进程,而一个程序中的多个任务则被称为线程。

    进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位。

    1.多线程的所有状态都在Thread中的State枚举中定义,如下所示:

    public enum State {
            /**
             * Thread state for a thread which has not yet started.
             */
            NEW,
    
            /**
             * Thread state for a runnable thread.  A thread in the runnable
             * state is executing in the Java virtual machine but it may
             * be waiting for other resources from the operating system
             * such as processor.
             */
            RUNNABLE,
    
            /**
             * Thread state for a thread blocked waiting for a monitor lock.
             * A thread in the blocked state is waiting for a monitor lock
             * to enter a synchronized block/method or
             * reenter a synchronized block/method after calling
             * {@link Object#wait() Object.wait}.
             */
            BLOCKED,
    
            /**
             * Thread state for a waiting thread.
             * A thread is in the waiting state due to calling one of the
             * following methods:
             * <ul>
             *   <li>{@link Object#wait() Object.wait} with no timeout</li>
             *   <li>{@link #join() Thread.join} with no timeout</li>
             *   <li>{@link LockSupport#park() LockSupport.park}</li>
             * </ul>
             *
             * <p>A thread in the waiting state is waiting for another thread to
             * perform a particular action.
             *
             * For example, a thread that has called <tt>Object.wait()</tt>
             * on an object is waiting for another thread to call
             * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
             * that object. A thread that has called <tt>Thread.join()</tt>
             * is waiting for a specified thread to terminate.
             */
            WAITING,
    
            /**
             * Thread state for a waiting thread with a specified waiting time.
             * A thread is in the timed waiting state due to calling one of
             * the following methods with a specified positive waiting time:
             * <ul>
             *   <li>{@link #sleep Thread.sleep}</li>
             *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
             *   <li>{@link #join(long) Thread.join} with timeout</li>
             *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
             *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
             * </ul>
             */
            TIMED_WAITING,
    
            /**
             * Thread state for a terminated thread.
             * The thread has completed execution.
             */
            TERMINATED;
        }

    2.绘制一张简单的状态图,观察一下线程的生命周期

    3.多线程的几种状态

      New状态表示刚刚开始,这种线程还没有开始执行。等到线程的start()方法调用时,才表示线程开始执行。

      当线程执行时,处于RUNNABLE状态,表示线程所需的一切资源都已经准备好了。

      当线程在执行过程中遇到了synchronized同步块,就会进入BLOCKED阻塞状态。这时线程就会暂停执行,直到获得请求的锁。

      WAITING和TIME_WAITING都表示等待状态,它们的区别是WAITING会进入一个无时间限制的等待,TIME_WAITING会进行一个有限的等待。

      wait()方法等待的线程在等待notify()方法,而通过join()方法等待的线程则会等待目标线程的终止。

      当线程执行完毕后,则进入TERMINATED状态,表示结束。

    4.线程常用方法

    方法 说明
    setPriority(int newPriority) 更改线程的优先级
    static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
    void join() 等待该线程终止
    static void yield() 暂停当前正在执行的线程对象,并执行其他线程
    void stop() 线程中断,已被弃用,因为它是直接中断,相当于直接给你停电,这种方式太暴力,不安全
    void interrupt() 中断线程,非直接中断,而是将当前线程标志为中断标记,只是一个标记
    boolean isAlive() 测试线程是否处于活动状态

    5.停止线程:

    • 不推荐使用JDK提供的stop()、destroy()方法。【已弃用】
    • 推荐线程自己停止下来
    • 建议使用一个标志位进行终止变量,当flag=false时,则终止线程运行;换句话说就是一个变量控制是否线程还要继续;当线程需要停止的时候,flag=false;让这个线程正常结束。
    public class ThreadSafe extends Thread {
        public volatile boolean exit = false; 
            public void run() { 
            while (!exit){
                //do something
            }
        } 
    }

    6.线程休眠:

    • sleep(时间)指定当前线程阻塞的毫秒数;
    • sleep存在异常InterruptedException;
    • sleep时间达到后线程进入就绪状态;
    • sleep可以模拟网络延时,倒计时等;
    • 每个对象都有一个锁,sleep不会释放锁;

    线程休眠 实例代码:

    /**
     * desc   : 模拟网络延时:方法问题的发生性(多个线程同时操作同一个资源对象时,是线程不安全的)
     * version: 1.0
     */
    public class TestDemo implements Runnable {
        //票数
        private int ticketNums = 10;
    
        @Override
        public void run() {
            while (true) {
                if (ticketNums <= 0) {
                    break;
                }
                //模拟延时
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 拿到了第:" + ticketNums-- + " 张票");
            }
        }
    
        public static void main(String[] args) {
    
            TestDemo testSleep = new TestDemo();
    
            new Thread(testSleep, "小明").start();
            new Thread(testSleep, "老师").start();
            new Thread(testSleep, "黄牛党").start();
        }
    }

    运行结果:

    出现-1张票,所以多个线程同时操作同一个资源对象,是不安全的。

    7.线程礼让:

    • 礼让线程,让当前正在执行的线程暂停,但不阻塞
    • 将线程从运行状态转为就绪状态
    • 让CPU重新调度,礼让不一定成功!看CPU心情

    线程礼让 实例代码:

    /**
     * desc   : 测试礼让线程
     * <p>
     * 礼让不一定成功,看CPU心情
     * version: 1.0
     */
    public class TestDemo {
        public static void main(String[] args) {
            MyYield myYield = new MyYield();
            new Thread(myYield, "a").start();
            new Thread(myYield, "b").start();
        }
    }
    
    class MyYield implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "线程开始执行");
            Thread.yield();//礼让
            System.out.println(Thread.currentThread().getName() + "线程停止执行");
        }
    }

    运行结果:

    所以礼让,有可能成功,有可能不成功。

    8.合并线程:

    • Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞。
    • 可以想象成插队。
    • 会让线程阻塞,尽量少用

    线程合并 实例代码:

    /**
     * desc   : 测试Join方法,想象成插队,非常霸道
     * version: 1.0
     */
    public class TestDemo implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("线程Vip来了" + i);
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            //启动我们的线程
            TestDemo testJoin = new TestDemo();
            Thread thread = new Thread(testJoin);
            thread.start();
    
            //主线程
            for (int i = 0; i < 1000; i++) {
                if (i == 200) {
                    thread.join();//插队(这里一插队,就是当主线程执行到i==200的时候,就让子线程去执行打印“线程Vip来了”,必须等插队的那个子线程打印100条“线程Vip”执行完了,再继续执行主线程没有执行完的语句。)
                }
                System.out.println("main" + i);
            }
        }
    }

    9.线程状态观测:

    • Thread.State

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

    • NEW  尚未启动的线程处于此状态
    • RUNNABLE  在Java虚拟机中执行的线程处于此状态
    • BLOCKED  被阻塞等待监视器锁定的线程处于此状态
    • WAITING  正在等待另一个线程执行特定动作的线程处于此状态
    • TIMED_WAITING  正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
    • TERMINATED  已退出的线程处于此状态

    一个线程可以在给定的时间点处于一个状态。这些状态是不反应任何操作系统线程状态的虚拟机状态。

    线程状态观测 实例代码:

    /**
     * desc   : 观察测试线程的状态
     * version: 1.0
     */
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("///////");
                }
            });
    
            //观察状态
            Thread.State state = thread.getState();
            System.out.println(state);//NEW
    
            //观察启动后
            thread.start();
            state = thread.getState();
            System.out.println(state);//Run
    
            while (state != Thread.State.TERMINATED) {//只要线程不终止,就一直输出
                Thread.sleep(100);
                state = thread.getState();//更新线程状态
                System.out.println(state);//输出状态
            }
        }
    }

    运行结果:

    需要注意的是,死亡之后的线程是不能再次启动,否则会报错。

    10.线程优先级:

    • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
    • 线程的优先级用数字表示,范围从1~10.

        Thread.MIN_PRIORITY=1;

        Thread.MAX_PRIORITY=10;

        Thread.NORM_PRIORITY=5;

    • 使用一下方式改变或获取优先级

        getPriority(); setPriority(int xxx)

    线程优先级 实例代码:

    /**
     * desc   : 测试线程的优先级
     * version: 1.0
     */
    public class TestDemo extends Thread {
        public static void main(String[] args) {
            //主线程 默认优先级 为5
            System.out.println(Thread.currentThread().getName() + "--->" + Thread.currentThread().getPriority());
    
            MyPriority myPriority = new MyPriority();
    
            Thread thread1 = new Thread(myPriority);
            Thread thread2 = new Thread(myPriority);
            Thread thread3 = new Thread(myPriority);
    
            //先设置优先级,再启动,不设置优先级,默认为5
            thread1.start();
    
            thread2.setPriority(3);
            thread2.start();
    
            thread3.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY = 10
            thread3.start();
        }
    }
    
    class MyPriority implements Runnable {
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "--->" + Thread.currentThread().getPriority());
        }
    }

    运行结果:

    可以看到,设置优先级,但却不一定说优先级高的就一定能先执行。因为优先级低的线程,只是说它被CPU调度的概率低,并不是说优先级低就不会被调度到,这个决定权还是得看CPU。

    11.守护线程(daemon):

    • 线程分为用户线程守护线程
    • 虚拟机必须确保用户线程执行完毕
    • 虚拟机不用等待守护线程执行完毕
    • 如:后台记录操作日志,监控内存,垃圾回收等待..

    可以通过setDaemon(true)把线程设置成守护线程, 默认是false,表示用户线程,正常的线程都是用户线程。

    守护线程 实例代码:

    public class TestDemo {
    
    
        /**
         * desc   : 测试守护线程
         * <p>
         * version: 1.0
         */
    
        public static void main(String[] args) {
            God god = new God();
            You you = new You();
    
            Thread thread = new Thread(god);
            thread.setDaemon(true);//默认是false,表示用户线程,正常的线程都是用户线程
            thread.start();//上帝 守护线程启动...
    
            new Thread(you).start();//你 用户线程启动...
        }
    }
    
    //上帝
    class God implements Runnable {
    
        @Override
        public void run() {
            while (true) {//因为虚拟机不用等待守护线程执行完毕,所以这里直接给了一个true条件
                System.out.println("上帝保佑着你");
            }
        }
    }
    
    //
    class You implements Runnable {
    
        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println("你一生都开心地或者");
            }
            System.out.println("=======goodbye! world!======");//hello ,world
        }
    }

    程序只要当用户线程执行完毕后,就会停止,程序不会根据守护线程是否执行完毕,而是根据用户线程,用户线程执行完了,程序也就结束了。

  • 相关阅读:
    git stash回退
    基于git tag快速修复
    Go语言开发规范
    defer实现原理
    string数据结构
    关于const iota
    struct
    map数据结构底层详解
    go 函数进阶
    go 内存分配原理
  • 原文地址:https://www.cnblogs.com/TimeSay/p/12342143.html
Copyright © 2011-2022 走看看