0


流水的语言,铁打的Java,Java 天生就是多线程

Java 天生就是多线程


前言

请添加图片描述


一、Java 中的线程

一个Java 程序从main() 方法开始执行,然后按照既定的代码逻辑执行,看似没有其他线程参与,但实际上Java
程序天生就是多线程程序,因为执行main() 方法的是一个名称为main 的线程。

public static void main(String[] args){//        java 虚拟机线程系统的管理接口
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();//        不需要获取同步的monitor 和synchronizer 信息,仅仅获取线程和线程堆栈信息
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false,false);//        遍历线程,仅打印线程ID 和线程名称信息for(ThreadInfo threadInfo : threadInfos){
            System.out.println("线程ID"+ threadInfo.getThreadId()+"线程名"+ threadInfo.getThreadName());}}

上面代码输出的结果:

在这里插入图片描述

  1.                                          M                            o                            n                            i                            t                            o                            r                            C                            t                            r                            l                            −                            B                            r                            e                            a                            k                                       \textcolor{red}{Monitor Ctrl-Break}                  MonitorCtrl−Break 监控 Ctrl-Break 中断信号的
    
  2.                                          S                            i                            g                            n                            a                            l                            D                            i                            s                            p                            a                            t                            c                            h                            e                            r                                       \textcolor{red}{Signal Dispatcher}                  SignalDispatcher 分发处理发送给 JVM 信号的线程
    
  3.                                          F                            i                            n                            a                            l                            i                            z                            e                            r                                       \textcolor{red}{ Finalizer }                  Finalizer 调用对象 finalize 方法的线程
    
  4.                                          R                            e                            f                            e                            r                            e                            n                            c                            e                            H                            a                            n                            d                            l                            e                            r                                       \textcolor{red}{Reference Handler}                  ReferenceHandler 清除 Reference 的线程
    
  5.                                          m                            a                            i                            n                                       \textcolor{red}{ main }                  main main 线程,用户程序入口
    

从上面的例子中,我们能发现,在Java中短短的几行代码,就给我们启动了5个线程,当然,不同的版本,启动的线程数量也不一样,由此我们可以得出:Java
天生就是多线程的

1、启动

线程的启动方式有两种(源码中的注释是这么写的)参见代码:cn.enjoyedu.ch1.base.NewThread:

  1. X extends Thread;,然后 X.start
  2. X implements Runnable;然后交给 Thread 运行

示例代码:(派生自Thread 类,来实现我们的两种线程启动方式)

/**
     * 扩展自Thread 类
     */private static class UserThread extends Thread{@Overridepublic void run(){
            System.out.println("UserThread.run");}}/**
     * 扩展自 Runnable 类
     */private static class UserRunnable implements Runnable {@Overridepublic void run(){
            System.out.println("UserRunnable.run");}}public static void main(String[] args){

        UserThread userThread = new UserThread();
        userThread.start();

        UserRunnable userRunnable = new UserRunnable();

        new Thread(userRunnable).start();}

Thread 和 Runnable 的区别:

  • Thread 是Java 里对线程的唯一抽象。
  • Runnable 是Java对任务(业务逻辑)的抽象。
  • Thread 可以接受任意一个 Runnable 的实例并执行。

2、中止

  • 线程自然终止:要么是run 执行完成了,要么是抛出了一个未处理的异常导致线程提前结束。
  • stop:暂停、恢复和停止操作对应在线程ThreadAPI 就是 suspend()、resume() 和 stop()。但是这些API 都是过期的,不再建议使用。不建议使用的主要原因有:以suspend()方法为例,在调用后,线程不会释放已占有的资源(比如锁),而是占有资源进入睡眠状态,这样容易引发死锁问题。同样,stop() 方法在终结一个线程时,不会保证线程的资源正常释放,通常是没有给予线程完成资源释放的机会,因此会到导致程序可能工作在不确定的状态下。整因为suspend()、resume() 和 stop() 方法带来的副作用,这些方法才会被标注为不建议使用的过期方法中。
  • 中断:安全的中止则是其它线程通过调用线程A的interrupt() 方法对其进行中止操作,中断代表着其它线程对A线程打了个招呼,“A, 你要中断了”,不代表线程A 会立即停止自己的工作,同样A线程可以不理会这种请求。因为Java 中的线程是协作式的,不是抢占式。线程通过检查自身的中断标志位是否被置为true来进行响应。
private static class UserThread extends Thread{publicUserThread(String name){super(name);}@Overridepublic void run(){
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName +"interrupt flag = "+isInterrupted());while(!isInterrupted()){//            while (!Thread.interrupted()){//            while (true){
                System.out.println(threadName+"is running");
                System.out.println(threadName+"inner interrupt flag = "+isInterrupted());}
            System.out.println(threadName+"interrupt flag = "+isInterrupted());}}public static void main(String[] args){
        Thread endTread = new UserThread("endTread");
        endTread.start();try{
            Thread.sleep(20);}catch(InterruptedException e){
            e.printStackTrace();}//        中断线程, 其实设置线程的标识位
        endTread.interrupt();}

运行上面的代码:

在这里插入图片描述

  • 我们发现,在使用isInterrupted() 进行线程中断的之后,isInterrupted()会返回一个true。我们一起来看一下 isInterrupted() 方法的源码:

在这里插入图片描述

  • 我们再使用一下静态的 interrupted() 方法,他返回的也是一个bool 值,先看一下这个方法的源码:

在这里插入图片描述

  • 从源码中我们发现,它返回也是中断标识符,但是,它把中断标识符给重新赋值成了true。我们来看一下运行效果

在这里插入图片描述

由此我们可以总结出:**线程通过方法

isInterrupted()

来进行判断是否被中断,也可以调用静态方法

Thread.interrupted()

来进行判断当前线程是否被中断,不过

Thread.interrupted()

会同时将中断标识位改写为

false

。**

3、阻塞

  • 如果一个线程处于阻塞状态(如线程调用了 thread.sleep、thread.join、 thread.wait 等),则线程在检查中断标识时,如果发现中断标识位true,则会在这些阻塞方法调用处抛出 InterruptedException 异常,并且在抛出异常后会立即将线程的中断标识位清除,即重新设置为true
private static class UserThread extends Thread{publicUserThread(String name){super(name);}@Overridepublic void run(){
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName +"interrupt flag = "+isInterrupted());while(!isInterrupted()){try{
                    Thread.sleep(100);}catch(InterruptedException e){
                    System.out.println(threadName+"inner interrupt flag = "+isInterrupted());
                    e.printStackTrace();}

                System.out.println(threadName+"is running");}
            System.out.println(threadName+"interrupt flag = "+isInterrupted());}}public static void main(String[] args){
        Thread endTread = new UserThread("endTread");
        endTread.start();try{
            Thread.sleep(20);}catch(InterruptedException e){
            e.printStackTrace();}//        中断线程, 其实设置线程的标识位
        endTread.interrupt();}

上面代码运行结果:

在这里插入图片描述

  • 那么像这种,我们该怎么去中中断操作呢?只需要在catch 中调用 interrupt() 方法就可以了

在这里插入图片描述

代码运行结果:

在这里插入图片描述

4、深入理解run 和 start

在这里插入图片描述

  • Thread 类是Java 里对线程概念的抽象,可以这样理解:我们通过new Thread() 其实只是new 出一个thread 的实例,还没有和操作系统中真正的线程挂起勾来。只有执行了start() 方法后,才实现了真正意义上的启动线程。
  • start() 方法让一个线程进入就绪队列等待分配CPU,分到CPU 后才调用run()方法,start() 方法不能重复调用,如果重复调用,就会抛出异常。
  • run() 方法是业务逻辑实现的地方,本质上和任意一个类的任意一个成员方法并没有任何区别,可以重复执行,也可以单独调用。

那么start() 和 run() 有什么区别呢?请看代码:

private static class UserThread extends Thread{@Overridepublic void run(){
            int i =90;while(i >0){try{
                    Thread.sleep(1000);}catch(InterruptedException e){
                    e.printStackTrace();}

                System.out.println("I am "+Thread.currentThread().getName()+"and now the i="+ i--);}}}public static void main(String[] args){
        Thread endTread = new UserThread();
        endTread.setName("threadRun");
        endTread.start();}

代码运行结果:**(观察运行结果,我们可以得出,调用start() 方法的时候,执行start() 方法的是子线程)**

在这里插入图片描述

我们修改一下代码,调用

run()

方法

public static void main(String[] args){
        Thread endTread = new UserThread();
        endTread.setName("threadRun");
        endTread.run();}

查看运行结果:**(观察运行结果,我们可以得出,调用run() 方法的时候,执行run() 方法的是主线程)**

在这里插入图片描述

5、join 方法

  • join() 方法是把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行。
  static class Students implements Runnable {private Thread thread;publicStudents(Thread thread){this.thread = thread;}publicStudents(){}@Overridepublic void run(){
            System.out.println("学生开始排队打饭。。。。。");try{if(thread !=null) thread.join();//            休眠2 秒
                Thread.sleep(2000);}catch(InterruptedException e){
                e.printStackTrace();}

            System.out.println("学生打饭完成");}}

    static class Teacher implements Runnable {@Overridepublic void run(){try{//  休眠2 秒
                Thread.sleep(2000);}catch(InterruptedException e){
                e.printStackTrace();}

            System.out.println("老师开始打饭、、、、、");
            System.out.println(Thread.currentThread().getName()+"老师打饭完成。");}}public static void main(String[] args) throws InterruptedException {
        Teacher teacher = new Teacher();
        Thread teaThread = new Thread(teacher);
        Students students = new Students(teaThread);
        Thread stuThread = new Thread(students);
        stuThread.start();
        teaThread.start();

        System.out.println("我开始打饭、、、、、");
        stuThread.join();//         让主线程休眠2 秒
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName()+"我打饭完成");}

代码运行结果如下:

在这里插入图片描述

由上代码运行结果,我们可以得出:**在线程B中调用了线程A的

join()

方法,只到线程A 执行完毕后,才会继续执行线程B的。**

6、线程优先级

  • 在 Java 线程中,通过一个整型成员变量 priority 来控制优先级,优先级的范 围从 1~10,在线程构建的时候可以通过 setPriority(int)方法来修改优先级,默认 优先级是 5,优先级高的线程分配时间片的数量要多于优先级低的线程。
  • 设置线程优先级时,针对频繁阻塞(休眠或者 I/O 操作)的线程需要设置较 高优先级,而偏重计算(需要较多 CPU 时间或者偏运算)的线程则设置较低的 优先级,确保处理器不会被独占。在不同的 JVM 以及操作系统上,线程规划会 存在差异,有些操作系统甚至会忽略对线程优先级的设定。

7、守护线程

  • Daemon(守护)线程是一种支持型线程,因为它主要被用作程序中后台调 度以及支持性工作。这意味着,当一个 Java 虚拟机中不存在非 Daemon 线程的 时候,Java 虚拟机将会退出。可以通过调用 Thread.setDaemon(true)将线程设置 为 Daemon 线程。我们一般用不上,比如垃圾回收线程就是 Daemon 线程。
  • Daemon 线程被用作完成支持性工作,但是在 Java 虚拟机退出时 Daemon 线 程中的 finally 块并不一定会执行。在构建 Daemon 线程时,不能依靠 finally 块中 的内容来确保执行关闭或清理资源的逻辑。

8、synchronized 内置锁

  • 线程开始运行,拥有自己的栈空间,就如同一个脚本一样,按照既定的代码 一步一步地执行,直到终止。但是,每个运行中的线程,如果仅仅是孤立地运行, 那么没有一点儿价值,或者说价值很少,如果多个线程能够相互配合完成工作, 包括数据之间的共享,协同处理事情。这将会带来巨大的价值。
  • Java 支持多个线程同时访问一个对象或者对象的成员变量,关键字 synchronized 可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线 程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量 访问的可见性和排他性,又称为内置锁机制。

下面我们看一段代码,在main 方法中启动两个线程,每个线程的count 值都是10000,两个线程的和应该就是20000。

publicclass OnlyMain {private long count =0;private Object object= new Object();public long getCount(){return count;}public void incCount(){
        count++;}//    线程private static class Count extends Thread {private OnlyMain onlyMain;publicCount(OnlyMain onlyMain){this.onlyMain = onlyMain;}@Overridepublic void run(){for(int i =0; i <10000; i++){//            count = count++ = 10000
                onlyMain.incCount();}}}public static void main(String[] args) throws InterruptedException {
        OnlyMain onlyMain = new OnlyMain();//        启动两个线程
        Count count1 = new Count(onlyMain);
        Count count2 = new Count(onlyMain);

        count1.start();
        count2.start();

        Thread.sleep(50);

        System.out.println(onlyMain.count);}}

代码的运行结果是:

在这里插入图片描述

经过多次运行,每次运行的结果都不一样,只有在很少很少的几率的情况下,才会出现正确的20000结果值,这是为什么呢?

  • 这是因为,两个线程同时对count 成员变量进行访问,才导致输出结果的错误。怎么解决呢?使用synchronized 内置锁。

修改上面代码中的incCount() 方法,添加一个内锁:

public synchronized void incCount(){
        count++;}

这样我们就能保证每次运行的正确结果了:

在这里插入图片描述

9、对象锁和类锁

在这里插入图片描述

在这里插入图片描述

  • 对象锁是用于对象实例方法的锁,或者一个对象实例上,类锁 是用于类的静态方法或一个类的class 上的,我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所有不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。
  • 但是有一点必须要注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,类锁其实锁的是每个类的对应的class 对象。类锁和对象锁之间也是互不干扰的。

总结

🎉

      原
     
     
      创
     
     
      不
     
     
      易
     
     
      ,
     
     
      还
     
     
      希
     
     
      望
     
     
      各
     
     
      位
     
     
      大
     
     
      佬
     
     
      支
     
     
      持
     
     
      一
     
     
      下
     
    
   
   
    \textcolor{blue}{原创不易,还希望各位大佬支持一下}
   
  
 原创不易,还希望各位大佬支持一下

👍

      点
     
     
      赞
     
     
      ,
     
     
      你
     
     
      的
     
     
      认
     
     
      可
     
     
      是
     
     
      我
     
     
      创
     
     
      作
     
     
      的
     
     
      动
     
     
      力
     
     
      !
     
    
   
   
    \textcolor{green}{点赞,你的认可是我创作的动力!}
   
  
 点赞,你的认可是我创作的动力!

🌟

      收
     
     
      藏
     
     
      ,
     
     
      你
     
     
      的
     
     
      青
     
     
      睐
     
     
      是
     
     
      我
     
     
      努
     
     
      力
     
     
      的
     
     
      方
     
     
      向
     
     
      !
     
    
   
   
    \textcolor{green}{收藏,你的青睐是我努力的方向!}
   
  
 收藏,你的青睐是我努力的方向!

✏️

      评
     
     
      论
     
     
      ,
     
     
      你
     
     
      的
     
     
      意
     
     
      见
     
     
      是
     
     
      我
     
     
      进
     
     
      步
     
     
      的
     
     
      财
     
     
      富
     
     
      !
     
    
   
   
    \textcolor{green}{评论,你的意见是我进步的财富!}
   
  
 评论,你的意见是我进步的财富!
标签: java kotlin android

本文转载自: https://blog.csdn.net/u010755471/article/details/124941319
版权归原作者 不爱学习的猪 所有, 如有侵权,请联系我们删除。

“流水的语言,铁打的Java,Java 天生就是多线程”的评论:

还没有评论