多线程演示并发与串行的效率、Java中的Thread类、如何中断一个线程

多线程提升代码效率演示-代码体现

多线程是用来实现并发编程,以期望来提升代码的运行效率。
下面通过代码举例:
我们在主线程中计算a和b的值对比用多线程计算a和b的值所需要的时间。

package 线程;

public class ThreadDemo91 {

      private static final long count=10_0000_0000;
      
      public static void main(String[] args) throws InterruptedException{
      
          //调用并发
          concurrency();
          //调用串行
          serial();
      }
      private static void concurrency() throws InterruptedException{
          long begin =System.nanoTime();
          //利用一个线程计算a的值
          Thread thread =new Thread(new Runnable() {
              @Override
              public void run() {

                      int a=0;
                      for(long i=0;i<count;i++){
                          a--;
                      }
              }
          });
          thread.start();
          //主线程内计算b的值
          int b=0;
          for(long i=0;i<count;i++){
              b--;
          }
          //等待线程thread运行结束
          thread.join();
          //统计耗时
          long end=System.nanoTime();
          double ms=(end-begin)*1.0/1000/1000;
          System.out.printf("并发:%f 毫秒%n",ms);
      }
      private static void serial(){
          //全部在主线程内计算a、b的值
          long begin=System.nanoTime();
          int a=0;
          for(long i=0;i<count;i++){
              a--;
          }
          int b=0;
          for(long i=0;i<count;i++){
              b--;
          }
          long end=System.nanoTime();
          double ms=(end-begin)*1.0/1000/1000;
          System.out.printf("串行:%f 毫秒%n",ms);
      }
}

运行结果如下:在这里插入图片描述

  • 当并发次数操作不超过百万次时,速度会比串行要慢一些,原因是线程创建和上下文切换需要时间
  • 当次数比较大的时候,并发优势非常明显
JDK中观察线程信息

我们在java代码中通过创建的线程,可以通过JDK内置的工具jconcole进行观察。
这个工具在我们安装jdk1.8的bin目录下,如下图:
在这里插入图片描述
在这里插入图片描述
双击这个jconsole.exe文件,进入如下界面:
在这里插入图片描述
选择本地进程,选择当前正在运行的线程,我这里正在运行的线程是ThreadDemo90,选择,然后点击连接
在这里插入图片描述
继续点击。
在这里插入图片描述

在这里插入图片描述
我的ThreadDemo90代码如下:

package 线程;

public class ThreadDemo90 {
    public static void main(String[] args) throws InterruptedException {
       Thread t=new Thread(new Runnable() {
           @Override
           public void run() {
               while(true){
                   System.out.println("7827346");
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
       },"Mythread");
       
        t.start();
        
        while(true){
            System.out.println("dui");
            Thread.sleep(1000);
        }
    }
}

Thread类中方法介绍

常见的构造方法如下:
在这里插入图片描述
每个构造方法对应的代码如下:

       //构造方法1
        Thread t1=new Thread();
        //构造方法2
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                //do somthing
            }
        });
         //构造方法3
        Thread t3=new Thread("我的线程名字");
          //构造方法4
          
        Thread t4=new Thread(new Runnable(){

            @Override
            public void run() {
                
            }
        },"我的线程名字");

Thread类对象有name最大的意义,就是方便调试~

Thread对象的相关属性

在这里插入图片描述
isDaemon():其中isDaemon方法用来判断是否是后台线程,JVM会在一个进程的所有非后台线程结束后才会结束运行。该线程是否是后台线程,影响了JVM进程是否能够退出~
我们在代码中创建的线程一般默认不是后台线程。此时,如果main方法结束了,线程还没结束,JVM进程不会结束。如果当前线程是后台线程,此时,如果main方法结束了,线程还没结束,JVM进程就会直接结束,同时也会结束这个后台线程,一起带走~
isAlive():另外我们通过isAlive()方法来判断当前线程是否存活,这个方法判定的依据就是内核中的PCB是否销毁,系统中的线程是否销毁~
PCB(process control block)就是操作系统中的进程管理模块,可以这么理解,一个线程的基础就是一个PCB,一个进程中可能含有多个PCB,每次创建一个新的线程,就产生一个新的PCB。这个pcb中含有控制该线程的全部信息。
我们在java进程中通过Thread创建了一个新的对象,通过调用该对象start方法,就会在内核态中创建出一个PCB,然后这个PCB就会在内核中参与调度和执行,就开始执行Thread对象中的代码,当代码执行完毕后,这里的PCB(内核中的线程)随之就销毁了~
如下图:
在这里插入图片描述

相关属性的使用方法如下:

package 线程;

public class ThreadDemo89 {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            for(int i =0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+":我还或者");
                try {
                    Thread.sleep(1*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+":我即将死去");
        });
        System.out.println(Thread.currentThread().getName()+":ID:"+thread.getId());
        System.out.println(Thread.currentThread().getName()+":名称:"+thread.getName());
        System.out.println(Thread.currentThread().getName()+":状态:"+thread.getState());
        System.out.println(Thread.currentThread().getName()+":优先级:"+thread.getPriority());
        System.out.println(Thread.currentThread().getName()+":后台线程:"+thread.isDaemon());
        System.out.println(Thread.currentThread().getName()+":活着:"+thread.isAlive());
        System.out.println(Thread.currentThread().getName()+":被中断:"+thread.isInterrupted());
    }
}

运行结果如下:
在这里插入图片描述

如何中断一个线程
方法1:共享标记中断

我们可以设置一个静态boolean变量作为标记,这样main线程和新创建的线程都可以共享这个变量,当a线程在执行的时候,我们可以在b线程中修改a和b共享变量,让a线程停下。
代码如下:

package 线程;

public class ThreadDemo88 {
    private static boolean flg=true;
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(){
            @Override
            public void run() {
               while(flg){
                   System.out.println("线程运行中...");
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
                System.out.println("线程结束!!");
            }
        };
        t.start();

        //主循环中也等待3秒
        Thread.sleep(6000);

        //3秒中之后,就把flag改成false;
        flg=false;
    }
}

运行结果如下:
在这里插入图片描述

通过本段代码我们可以看到,我们在main线程中修改flg的值,就可以控制t线程停止。

方法2:interrupt方法

其实Thread类中本身就内置了一个boolean变量作为线程是否被中断的标记。
一般我们需要调用isInterrupted()方法来判断当前线程的标记位。未被中断的线程一般的标记位都默认是false;然后可以通过interrupt方法来修改当前的标记位。但如果当前线程处于sleep、wait或者join等情况时,一般会抛出一个 InterruptedException异常,提醒我们当前线程的状态不是正在运行的状态,而是休眠,等待或者被阻塞的状态。这个时候就需要在异常抛出后的处理代码中做一些操作,像break,return等都可以及时终止循环,让线程停下来。
注意:run()方法执行完毕,线程就会被销毁。我们所说的让线程停止/中断,其实本质就是怎么让run方法中的代码停止执行,或者说快速执行完。
使用代码如下:

package 线程;

public class ThreadDemo87 {

    public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run() {
            //Thread.currentThread().isInterrupted() 当前线程的标记为一般都是false,因为是处于一个未被打断的状态。
               while(!Thread.currentThread().isInterrupted()){
                    System.out.println("线程运行中....");
                    try {
                    //每次休眠1秒钟
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                      //在这里加一个break,就能保证这个循环结束~
                      //加了一个break,就能保证interrupt方法在当前线程阻塞的时候抛出异常后,
                      //继续执行catch里的代码时,能及时终止循环,让这个线程结束。
                        break;
                    }
                }
            }
        };
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();

    }
}

注意区别两个方法:
isInterrupted()和interrupted()方法.

  • isInterrupted()是Thread 的实例方法。
  • interrupted()是Thread的类方法。前面的修饰是有static的。
  • 这两个方法功能类似,都可以用来判断线程的标记位是否被修改,或者说被设置。
    == 当我们用interrupt方法把标记位设置成true后,此时,注意,是将一般线程默认的标记位false设置成true后,此时如果调用isInterrupted()方法,该方法就会告诉你,当前的标记位是true,并且把标记位修改为false,这样下一次查看标记位就变成了false。但如果此时调用的是interrupted()方法,该方法也会告诉你当前的标记位是true,但是不会修改标记位。下一次还是true。==
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值