首页 > 其他分享 >线程的核心原理

线程的核心原理

时间:2024-07-26 09:25:00浏览次数:19  
标签:Thread 核心 System static 线程 println 原理 public

线程调度模型

1分时调度模型: 系统平均分配CPU时间片,所有线程轮流占用CPU.

2抢占式调度模型: 系统按照线程优先级来分配CPU时间片,优先级高的线程获取CPU执行时间相对多一些.

线程的优先级

Thread类里的这个属性private int priority代表线程的优先级.优先级值的范围为1-10.

    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

获取线程优先级的方法

public final int getPriority() {
        return priority;
    }

设置线程优先级的方法

public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

这个set方法和我们写的实体类有点些许不一样呀.因为线程的优先级取值是1-10,进行了传参的校验.还进行了线程组优先级的校验.checkAccess()这个方法是对安全方面的校验.虽然很繁琐,但是我还是觉着思想是好的.有约束才能成方圆嘛.

了解到这里,java其实线程模型采用的也是抢占式调度.空说无凭,难以服众.上案例.

public class PriorityThreadDemo {

    static class PriorityThread extends Thread {
        static int threadName = 1;

        //线程的名称.
        public PriorityThread() {
            super("thread-" + threadName);
            threadName++;
        }

        //线程的执行次数.
        long threadExecuteNum = 0;

        @Override
        public void run() {
            //死循环,看线程执行的次数,
            for (int i = 0; ; i++) {
                threadExecuteNum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        List<PriorityThread> threads = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            PriorityThread priorityThread = new PriorityThread();
            priorityThread.setPriority(i + 1);
            threads.add(priorityThread);
        }
        for (PriorityThread thread : threads) {
            thread.start();
        }

        Thread.sleep(1000);

        for (PriorityThread thread : threads) {
            thread.stop();
        }

        for (PriorityThread thread : threads) {
            System.out.println("线程优先级" + thread.getPriority() + "线程获取时间片" + thread.threadExecuteNum);
        }
    }
}

结果我就不贴出来.可以直接复制代码跑起来,就能看到结果.不过有一点还是要说明,优先级高并不代表一定就会百分百高于其他线程,就像抢占式解释的那样,只是概率高.

线程的生命周期

Thread类里的属性private volatile threadStatusint = 0;代表线程的状态.

NEW状态:创建成功但是还没有调用start方法.

RUNNABLE状态:调用了start方法以后,处于一个就绪状态或者执行状态.(就绪状态就是还未获得CPU执行权,运行态就是获得了CPU执行权)

TERMINATED状态: run方法执行完成以后变成终止状态,当线程执行过程中发生了异常没有被捕获也会进入终止状态.

TIMED_WAITING状态:线程进入限时等待状态.

处于限时等待状态的情况.

Thread.sleep(long n);使得线程进入限时等待,时间为n毫秒.

Object .wait();带时限的抢占对象monitor锁.

Thread.join();带时限的合并线程.

LockSupport.parkNanos();让线程等待时间为纳秒.

LockSupport.parkUntil();让线程等待时间可以灵活设置.

public class StatueThread {
    static List<Thread> threadList = new ArrayList<>();

    //线程名称.
    static int threadNum = 0;

    private static void printThreadStatues() {
        for (Thread threadDemo : threadList) {
            System.out.println("线程" + threadDemo.getName() + "的状态为" + threadDemo.getState());
        }
    }

    private static void addThreadStatues(Thread thread) {
        threadList.add(thread);
    }

    static class StatuesThreadDemo extends Thread {
        public StatuesThreadDemo() {
            super("线程--" + (++threadNum));
            //将自己加入集合中.
            threadList.add(this);
        }

        @Override
        public void run() {
            System.out.println("线程--" + this.getName() + "的状态为" + this.getState());
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                printThreadStatues();
            }
            System.out.println("线程--" + this.getName() + "运行结束");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        addThreadStatues(Thread.currentThread());
        StatuesThreadDemo threadDemo1 = new StatuesThreadDemo();
        System.out.println(threadDemo1.getName() + "--状态为--" + threadDemo1.getState());
        StatuesThreadDemo threadDemo2 = new StatuesThreadDemo();
        System.out.println(threadDemo2.getName() + "--状态为--" + threadDemo1.getState());
        StatuesThreadDemo threadDemo3 = new StatuesThreadDemo();
        System.out.println(threadDemo3.getName() + "--状态为--" + threadDemo1.getState());

        threadDemo1.start();
        Thread.sleep(5000);

        threadDemo2.start();
        Thread.sleep(5000);

        threadDemo3.start();
        Thread.sleep(1000);
    }
}

线程的基本操作

1线程名称在启动之前设置.但也允许为运行的线程设置名称.

2允许两个线程的名字相同,但应该避免.

3如果没有指定线程的名称,系统会自动设置线程的名称.

public class ThreadDemo {

    static class ThreadNameTarget implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "线程执行的轮次" + i);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadNameTarget threadNameTarget = new ThreadNameTarget();
        new Thread(threadNameTarget).start();
        new Thread(threadNameTarget).start();
        new Thread(threadNameTarget, "线程--A").start();
        new Thread(threadNameTarget, "线程--A").start();
        Thread.sleep(Integer.MAX_VALUE);
    }
}
线程的sleep操作

睡眠我的作用是让当前线程休眠,把cpu的执行权让给其他线程.线程状态从运行态变成阻塞状态.

Thread类中sleep方法是一组重载的静态方法.会有InterruptedException受检异常.

public static native void sleep(long millis) throws InterruptedException;

public static void sleep(long millis, int nanos) throws InterruptedException

具体的例子就不往出贴了.小伙伴们可以自行尝试体验下.

线程的interrupted操作.

java当中已经提供了stop()方法作为线程的停止操作,但是被设置为过时的操作.是因为无法知道当前线程处于什么状态,有可能持有某把锁,或者正在进行数据库操作,导致属于一致性问题等.才不建议使用stop()方法.

interrupted()方法的作用,如果线程处于阻塞状态(如果调用了Object.wait()方法),就会立马退出阻塞.并且抛出InterruptedException异常,线程可以捕获并且进行处理.Thread.join()和Thread.sleep()方法也会有同样的情况.

如果线程处于运行状态,线程就不会受任何影响,继续运行,只是线程中断标记被设置为true.在适当的位置调用isInterrupted()方法查看自己是否被中断,并执行退出操作.可以看出线程被打断抛出异常,停止执行后面的逻辑.(执行结果我就不贴出来了,小伙伴们可以自行复制代码体会,虽然案例很简单,但是思考的过程真的很重要,不仅仅是你们,我自己也是.)

public class InterruptedDemo {

    static int threadNum = 1;

    static class SleepThread extends Thread {
        //构造方法.
        public SleepThread() {
            super("sleepThread-" + threadNum);
            threadNum++;
        }

        @Override
        public void run() {
            System.out.println(getName() + "进入了睡眠");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println(getName() + "发生异常被打断");
                return;
            }
            System.out.println(getName() + "线程执行结束了.");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SleepThread thread1 = new SleepThread();
        thread1.start();
        SleepThread thread2 = new SleepThread();
        thread2.start();
        //主线程睡眠两秒.
        Thread.sleep(2000);
        //打断线程1.
        thread1.interrupt();
        //主线程等待五秒.
        Thread.sleep(50000);
        thread2.interrupt();

        Thread.sleep(1000);
        System.out.println("程序结束");
    }
}

 可以看出线程被打断抛出异常,停止执行后面的逻辑.(执行结果我就不贴出来了,小伙伴们可以自行复制代码体会,虽然案例很简单,但是思考的过程真的很重要,不仅仅是你们,我自己也是.)

public class RunThread {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程开始执行了");
                while (true) {
                    System.out.println(Thread.currentThread().isInterrupted());
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("线程结束了");
                        return;
                    }
                }
            }
        });
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
        Thread.sleep(2000);
        thread.interrupt();
    }
}

虽然run方法里是一个无条件不会停止的循环,但是通过标志位,还是会停止这个线程.

线程的join操作

线程join操作有三个版本. 

 public final void join() throws InterruptedException {
    }

 此方法会把当前线程变为WAITING状态.直到线程合并结束.

 public final synchronized void join(long millis)
    throws InterruptedException {}

 此方法会把当前线程变为TIME_WAITING状态,直到合并线程结束,或者被合并线程执行millis的时间.

public final synchronized void join(long millis, int nanos)
    throws InterruptedException {}

此方法会把当前线程变为TIME_WAITING状态,直到合并线程结束,或者被合并线程执行millis+nanos的时间.

join方法的要点.

1: join方法是一个实例方法,需要使用被合并线程的句柄(或者指针 变量)去调用,比如thread.join().

执行这段代码的当前线程为合并线程,会进入TIME_WAITING等待状态,让出CPU执行权,等待被合并线程执行完毕.

2: 如果设置了被合并线程的执行时间mills(或者mills+nanos),并不能保证当前线程一定会回复RUNNABLE状态.(java是抢占式调度,这样会好理解为什么).

3: 如果主动方合并线程在等待时被中断,就会抛出InterruptedException 受检异常.(受检异常和运行时异常可以研究研究,深入了解下)

说了这么多,感觉合并点还是有点模糊,合并点其实可以理解成就是A线程等待B线程执行完在开始的地方.

public class JoinThread {

    static int threadNum = 1;

    static class SleepThread extends Thread {
        //构造方法.
        public SleepThread() {
            super("sleepThread-" + threadNum);
            threadNum++;
        }

        @Override
        public void run() {
            System.out.println(getName() + "进入了睡眠");
            System.out.println(getName() + "线程执行结束了.");
        }
    }

    public static void main(String[] args) {
        SleepThread sleepThread1 = new SleepThread();
        System.out.println("线程启动.");
        sleepThread1.start();
        try {
            //主线程合并sleepThread1
            sleepThread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        SleepThread sleepThread2 = new SleepThread();
        sleepThread2.start();
        try {
            //主线程合并sleepThread2
            sleepThread2.join(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "线程结束.");
    }
}

从这个例子中就能看出来,等线程一和线程二执行完毕以后,主线程才会打印线程结束,而每个join方法就是合并点.

线程的yield操作.

线程的yield(让步)操作就是把当前线程持有的CPU执行权让出来,让其他线程去持有CPU,但是线程状态依旧是RUNNABLE状态,对应的操作系统就是从执行态转变为就绪态,所以从这里可以想象到,它刚让出CPU执行权也可以重新抢占.

yield()方法是Thread类提供的一个静态方法,它会让当前执行的线程暂停,但又不会阻塞当前线程,让线程进入就绪状态.

public class YieldThreadDemo {

    /**
     * 执行次数.
     */
    public static final int MAX_TURN = 100;
    /**
     * 执行编号.
     */
    public static AtomicInteger index = new AtomicInteger(0);
    /**
     * 执行总次数.
     */
    private static Map<String, AtomicInteger> metric = new HashMap<>();

    /**
     * 输出线程的总次数.
     */
    private static void printMetric() {
        System.out.println("metric=" + metric);
    }

    static class YieldThread extends Thread {
        static int threadNum = 1;

        public YieldThread() {
            super("YieldThread--" + threadNum);
            threadNum++;
            metric.put(this.getName(), new AtomicInteger(0));
        }

        @Override
        public void run() {
            for (int i = 1; i < MAX_TURN && index.get() < MAX_TURN; i++) {
                System.out.println(getName() + "线程优先级:" + getPriority());
                index.getAndIncrement();
                metric.get(this.getName()).getAndIncrement();
                if (i % 2 == 0) {
                    Thread.yield();
                }
                printMetric();
                System.out.println(getName() + "运行结束");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        YieldThread yieldThread1 = new YieldThread();
        yieldThread1.setPriority(Thread.MAX_PRIORITY);
        YieldThread yieldThread2 = new YieldThread();
        yieldThread2.setPriority(Thread.MIN_PRIORITY);
        System.out.println("启动线程.");
        yieldThread1.start();
        yieldThread2.start();
        Thread.sleep(1000);
    }
}

 线程的daemon操作

java里的线程主要分为守护线程和用户线程.守护线程可以理解为后台线程.主要在后台提供一下服务.比如垃圾回收线程.

1:守护线程的基本操作

实例属性daemon:保存一个线程实例的守护状态,默认为false.表示默认为用户线程.

实例方法setDaemon():获取线程的守护状态,判断这个线程是不是守护线程.

2:守护线程的操作演示

public class DaemonThread {
    //守护线程类.
    static class daemonThread extends Thread {
        public daemonThread() {
            super("daemonThread");
        }

        @Override
        public void run() {
            System.out.println("--daemon线程开始执行");
            for (int i = 1; ; i++) {
                System.out.println(">>轮次" + i);
                System.out.println("--守护状态为" + Thread.currentThread().isDaemon());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        daemonThread daemonThread = new daemonThread();
        daemonThread.setDaemon(true);
        daemonThread.start();
        Thread thread = new Thread(() -> {
            System.out.println(">>用户线程开始执行");
            for (int i = 0; i < 4; i++) {
                System.out.println(">>轮次" + i);
                System.out.println(">>线程状态为" + Thread.currentThread().isDaemon());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("用户线程结束.");
        }, "userThread");
        
        thread.start();
        System.out.println("守护状态为"+Thread.currentThread().isDaemon());
        System.out.println("线程结束");
    }
}

3:守护线程与用户线程的关系

从是否守护线程来分,分为用户线程和守护线程.本质区别上,二者与jvm虚拟机进程终止方向不同.用户线程和jvm进程属于主动关系.如果用户线程全部终止,jvm进程也会终止.守护线程和jvm进程属于被动关系,jvm进程终止了,所有的守护线程也会终止.

4:守护线程的要点

用户线程必须在启动之前就设置为守护线程,启动之后不能再将用户线程设置为守护线程,否则jvm会抛出一个InterruptedException异常.

守护线程存在被jvm强行终止的风险.所以守护线程尽量不要去访问系统资源,如文件句柄数据库连接等.

守护线程创建的线程也是守护线程.

好多事情在没有做之前总觉着很简单,但是做起来了才发现.总结才是最难得.但是还是有很多收获吧.坚持下去,虽然不懂,但是在次回首,会发现收获到了意向不到的快乐.

如果喜欢的话,大家可以搜索一下微信公众号.   心有九月星辰

标签:Thread,核心,System,static,线程,println,原理,public
From: https://blog.csdn.net/m0_68082638/article/details/140647033

相关文章

  • 多线程的创建方式
    线程的创建方式1:通过继承Thread类创建一个线程类.子类重写Thread的run方法即可.publicclassThreadTwoTestextendsThread{publicstaticvoidmain(String[]args){ThreadTwoTestthreadTwoTest=newThreadTwoTest();threadTwoTest.start();......
  • ScrollView实现原理分析
    ScrollView是Android中用于实现单向滚动功能的布局容器。它只能容纳一个子视图,并且能够使这个子视图在垂直方向(默认)或水平方向上滚动。下面我们将结合源码来分析ScrollView的实现原理。1.ScrollView类定义ScrollView继承自FrameLayout,这意味着它本身是一个布局容器,......
  • ViewPager2实现原理分析
    ViewPager2 是Android开发中用于实现水平滑动视图的组件,它是 ViewPager 的一个改进版,提供了更多的功能和更好的性能。下面,我们将结合源码来简要分析 ViewPager2 的实现原理。1.基本架构ViewPager2 的主要架构基于 RecyclerView,它利用了 RecyclerView 的滚动、布......
  • 第三章 进程线程模型
    第三章进程线程模型进程1、并发环境与多道程序设计(1)程序的顺序执行程序:指令或者语句序列;体现了某种算法;所以程序是顺序的特点:顺序性;封闭性;程序执行结果的确定性;程序结果的可再现性(2)多道程序设计定义:计算机能够同时处理多个具有独立功能的程序;以增强系统的处理能力和提高机......
  • 智能音箱的工作原理
    智能音箱的工作原理主要涉及到硬件和软件两个层面的协同工作,以及多个关键技术环节的配合。以下是对智能音箱工作原理的详细解析:一、硬件层面智能音箱的硬件组成通常包括主控芯片、麦克风阵列、扬声器、Wi-Fi模块和电源等部分。主控芯片:作为智能音箱的“大脑”,负责控制整个......
  • Java SE核心技术——2 Java基础语法
    一、关键字和保留字1.关键字关键字的定义和特点定义:被[java语言]赋予了特殊含义,用作专门用途的字符串。特点:关键字中所有字母都为小写。关键字不能用作变量名,方法名,类名,包名和参数。2.保留字定义:现在java尚未使用,但以后版本可能会作为关键字使用。自己使用时应避免使用。-......
  • JavaSE核心技术——3 流程控制语句结构
    一、顺序结构程序由上向下运行。二、分支结构1.if语句三种格式:2.switch-case注意:1.switch(表达式)中表达式的值必须是下述几种类型之一:byte,short,char,int,枚举(jdk5.0),String(jdk7.0);2.break语句用来在执行完一个case分支后使程序跳出switch语句块;如果没有break,程序会顺......
  • java的跨平台原理
    java的跨平台原理:Java跨平台的原理主要是通过Java虚拟机(JVM)来实现的。为啥需要跨平台:不同平台的机器码是不兼容的。在编译原理中,我们知道编译器将源代码翻译成特定平台的机器码,这样程序就可以在特定平台上运行。然而,不同平台的机器码是不兼容的,这就导致了跨平台的困难。......
  • Java SE 核心技术——java初识
    一、JDK、JRE和JVM1.JDK、JRE和JVM定义JDK​即Java开发工具包。JDK是用于Java开发的一套工具包,里面包含了Java的编译器javac、Java程序打包工具jar、Java程序运行环境JRE、文档生成工具javadoc以及很多用于开发的工具。JRE​JRE是运行Java程序所需的环境,包括JVM......
  • 多线程补充(上)
    线程安全问题首先,什么是线程安全问题呢?线程安全问题指的是,多个线程同时操作同一个共享资源的时候,可能会出现业务安全问题。场景:小明和小红是一对夫妻,他们有一个共享账户,余额是10万元,小红和小明同时来取钱,并且2人各自都在取钱10万元,可能出现什么问题呢?小明和小红假设都是......