• 进阶Java编程(2)线程常用操作方法


    线程常用操作方法

      多线程的主要操作方法都在Thread类中定义的。

    1,线程的命名和取得

      多线程的运行状态是不确定的,那么在程序的开发之中为了可以获取到一些需要使用到的线程就只能依靠线程的名字来进行操作。所以线程的名字是一个至关重要的概念,这样在Thread类之中就提供有线程名称的处理。

        ①构造方法:public Thread(Runnable target,String name);

        ②设置名字:public final void setName(String name);

        ③取得名字:public final String getName();

      对于线程对象的获得是不可能只是依靠一个this来完成的,因为线程的状态不可控,但是有一点是明确的,所有的线程都要执行【run()方法】,那么这个时候可以考虑获取当前线程,在Thread类里面提供有

      ·范例:观察线程的命名操作

     1 class MyThread implements Runnable{
     2     @Override
     3     public void run() {
     4         System.out.println(Thread.currentThread().getName());
     5     }
     6 }
     7 public class Main {
     8     public static void main(String[] args) {
     9         MyThread mt=new MyThread();
    10         new Thread(mt,"线程A").start();
    11         new Thread(mt).start();
    12         new Thread(mt,"线程B").start();
    13         new Thread(mt,"线程C").start();
    14         new Thread(mt,"线程D").start();
    15     }
    16 }

      当开发者为线程设置名字的时候就是要设置的名称,而如果没有设置名字,则会自动生成一个不重复的名字。这种自动的属性命名主要是依靠了static属性完成的,在Thread类里面定义有如下操作:

    1 private static int threadInitNumber;
    2 private static synchronized int nextThreadNum() {
    3     return threadInitNumber++;
    4 }

    ·范例:观察一个main线程程序

    1 public class Main {
    2     public static void main(String[] args) {
    3         MyThread mt=new MyThread();
    4         new Thread(mt,"线程对象").start();
    5         mt.run();
    6     }
    7 }
    1 main
    2 线程对象

      通过此时的代码可以发现当使用了【mt.run()】直接在主方法里面之中调用线程类对象中的【run()】方法所获得的线程的名字为【main】所以可以得出一个结论:主方法也是一个线程。那么现在的问题来了,所有的线程都是在进程上的划分,那么进程在哪里?每当使用Java命令执行程序的时候就表示启动了一个JVM的进程,一台电脑上可以启动若干个JVM进程都会有各自的线程。

      在任何的开发之中,主线程可以创建若干个子线程。创建子线程的目的是可以将一些复杂逻辑或者比较耗时的逻辑交由子线程处理。

      ·范例:子线程的处理

     1 public class Main{
     2     public static void main(String[] args) {
     3         System.out.println("1,执行操作任务一");
     4         new Thread(()->{
     5             int temp=0;
     6             for(int x=0;x<Integer.MAX_VALUE;x++){
     7                 temp += x;
     8             }
     9             System.out.println("2,执行操作任务二");
    10         }).start();
    11         System.out.println("3,执行操作任务三");
    12     }
    13 }

      主线程负责处理整体流程,而子线程负责处理耗时操作。

    2,线程休眠

      如果说现在希望某个线程可以暂缓执行,那么就可以使用休眠的处理,在Thread中定义的休眠的方法如下:

        ·休眠:public static void sleep(long millis) throws InterruptedException;

        ·休眠:public static void sleep(long millis,int nanos) throws InterruptedException;

      在进行休眠的时候有可能会产生中断异常【InterruptedException】,中断异常属于Exception的子类,所以证明该异常必须进行处理。

      ·范例:观察休眠处理

     1 public class Main{
     2     public static void main(String[] args) {
     3         new Thread(()->{
     4            for(int x=0;x<10;x++){
     5                System.out.println(Thread.currentThread().getName()+"、x="+x);
     6                try {
     7                    Thread.sleep(100);//暂缓执行
     8                } catch (InterruptedException e) {
     9                    e.printStackTrace();
    10                }
    11            }
    12         },"线程对象").start();
    13     }
    14 }

      ·范例:产生多个线程对象进行休眠处理休眠的主要特点是可以实现线程的自动唤醒,以继续进行后续的处理。但是需要注意的是,如果现在有多个线程对象,那么休眠也是有先后顺序的。

     1 public class Main{
     2     public static void main(String[] args) {
     3         Runnable run=()->{
     4             for(int x=0;x<10;x++){
     5                System.out.println(Thread.currentThread().getName()+"、x="+x);
     6                 try {
     7                     Thread.sleep(100);//暂缓执行
     8                 } catch (InterruptedException e) {
     9                     e.printStackTrace();
    10                 }
    11             }
    12         };
    13         for(int num=0;num<5;num++){
    14             new Thread(run,"线程对象-"+num).start();
    15         }
    16     }
    17 }

      此时将产生五个线程对象,并且这五个线程对象执行的方法体是相同的。此时从程序执行的感觉上来讲好像若干个程序一起进行了休眠,而后一起进行了自动唤醒,但是实际上是有差别的。

    3,线程中断

      在之前发现线程的休眠里面提供有一个中断异常,实际上就证明线程的休眠是可以打断的,而这种打断肯定是由其他线程完成的。

        ·判断线程是否被中断:public boolean isInterrupted();

        ·中断线程执行:public void interrupt();

      ·范例:观察线程的中断处理操作

     1 public class Main{
     2     public static void main(String[] args) throws InterruptedException {
     3         Thread thread=new Thread(()->{
     4             System.out.println("【需要睡觉保存体力】");
     5             try{
     6                 Thread.sleep(10000);//预计准备休眠10s
     7             }catch (InterruptedException e) {
     8                 System.out.println("【睡眠被打断】");
     9             }
    10 
    11             System.out.println("【睡觉结束可以继续工作了】");
    12         });
    13         thread.start();
    14         Thread.sleep(1000);
    15         if(! thread.isInterrupted()){//该线程中断了吗
    16             System.out.println("【有人来打扰我休息】");
    17             thread.interrupt();//中断执行
    18         }
    19     }
    20 }
    1 【需要睡觉保存体力】
    2 
    3 【有人来打扰我休息】
    4 
    5 【睡眠被打断】
    6 
    7 【睡觉结束可以继续工作了】

      所有正在执行的线程都是可以被中断的,中断线程必须进行异常处理。

    4,线程强制运行

      所谓的线程的强制执行指的是当满足于某些条件之后,某一个线程对象将可以一直独占资源,一直到该线程的程序执行结束。

      ·范例:观察一个没有强制执行的程序

     1 public class Main{
     2     public static void main(String[] args) throws InterruptedException {
     3         Thread thread=new Thread(()->{
     4             for(int x=0;x<100;x++){
     5                 System.out.println(Thread.currentThread().getName()+"执行、x="+x);
     6                 try {
     7                     Thread.sleep(100);
     8                 } catch (InterruptedException e) {
     9                     e.printStackTrace();
    10                 }
    11             }
    12         },"玩耍的线程");
    13         thread.start();
    14         for(int x=0;x<100;x++){
    15             Thread.sleep(100);
    16             System.out.println("【main线程】num="+x);
    17         }
    18     }
    19 }

      ·强制执行:public final void join() throws InterruptException;这个时候主线程和子线程都在交替执行着,但是如果说现在你希望主线程独占执行。那么我们就可以使用【Thread类】中方法强制执行。

     1 public class Main{
     2     public static void main(String[] args) throws InterruptedException {
     3         Thread mainThread = Thread.currentThread();
     4         Thread thread=new Thread(()->{//获得主线程
     5             for(int x=0;x<100;x++){
     6                 if(x==3){
     7                     try {
     8                         mainThread.join();//主线程要优先执行
     9                     } catch (InterruptedException e) {
    10                         e.printStackTrace();
    11                     }
    12                 }
    13                 System.out.println(Thread.currentThread().getName()+"执行、x="+x);
    14                 try {
    15                     Thread.sleep(100);
    16                 } catch (InterruptedException e) {
    17                     e.printStackTrace();
    18                 }
    19             }
    20         },"玩耍的线程");
    21         thread.start();
    22         for(int x=0;x<100;x++){
    23             Thread.sleep(100);
    24             System.out.println("【main线程】num="+x);
    25         }
    26     }
    27 }

       在进行线程强制执行的时候一定要获取强制执行线程对象之后才可以执行join()调用。

    5,线程礼让

      线程的礼让指的是现将资源让出去,让别的线程先执行。

        ·礼让:public static void yield();

      ·范例:使用礼让操作

     1 public class Main{
     2     public static void main(String[] args) throws InterruptedException {
     3         Thread mainThread = Thread.currentThread();
     4         Thread thread=new Thread(()->{//获得主线程
     5             for(int x=0;x<100;x++){
     6                 if(x%3==0){
     7                     Thread.yield();//线程礼让执行
     8                     System.out.println("礼让其他线程");
     9                 }
    10                 System.out.println(Thread.currentThread().getName()+"执行、x="+x);
    11                 try {
    12                     Thread.sleep(100);
    13                 } catch (InterruptedException e) {
    14                     e.printStackTrace();
    15                 }
    16             }
    17         },"玩耍的线程");
    18         thread.start();
    19         for(int x=0;x<100;x++){
    20             Thread.sleep(100);
    21             System.out.println("【main线程】num="+x);
    22         }
    23     }
    24 }

      礼让执行的时候每一次调用yield()方法都只会礼让一次当前的资源。

    6,线程优先级

      从理论上来讲线程的优先级越高越有可能先执行(越有可能先抢占到资源)。在Thread类里面针对于优先级的操作提供有如下的两个方法:

        ·设置优先级:public final void setPriority(int newPriority);

        ·获取优先级:public final int getPriority()

      在定义优先级的时候都是通过int型的数字来完成的,而对于此数字的选择在Thread类里面就定义有三个常量:

        ①最高优先级:public static final int MAX_PRIORITY10;

        ②中等优先级:public static final int NORM_PRIORITY5;

        ③最低优先级:public static final int MIN_PRIORITY1;

      ·范例:观察优先级

     1 public class Main{
     2     public static void main(String[] args) throws InterruptedException {
     3         Runnable run=()->{
     4             for(int x=0;x<10;x++){
     5                 try{
     6                     Thread.sleep(1000);
     7                 } catch(InterruptedException e){
     8                     e.printStackTrace();
     9                 }
    10                 System.out.println(Thread.currentThread().getName()+"执行");
    11             }
    12         };
    13         Thread threadA=new Thread(run,"线程对象A");
    14         Thread threadB=new Thread(run,"线程对象B");
    15         Thread threadC=new Thread(run,"线程对象C");
    16 
    17         threadA.setPriority(10);
    18         threadB.setPriority(1);
    19         threadC.setPriority(1);
    20 
    21         threadA.start();
    22         threadB.start();
    23         threadC.start();
    24     }
    25 }

      ·范例:主线程的优先级 

    1 public class Main{
    2     public static void main(String[] args) throws InterruptedException {
    3         Thread mainThread=Thread.currentThread();
    4         System.out.println(mainThread.getPriority());
    5     }
    6 }

      备注:优先级高的只是倾向于先执行,但是并不是一定优先执行。主线程属于中等优先级,而默认创建的线程也是中等优先级。

  • 相关阅读:
    C#中回滚TransactionScope的使用方法和原理
    CAS5.3服务器搭建与客户端整合SpringBoot以及踩坑笔记
    JSON对象、JSON字符串和Java对象互相转
    Java实体类如何映射到json数据(驼峰映射到json中的下划线)
    expected at least 1 bean which qualifies as autowire candidate
    IDEA target中没有class文件/target中有class没有yml文件/yml文件不显示叶子
    yml配置从nacos配置中心取数据(单个或多个),读读源码,寻找如何配置多个
    seata-server 1.3.0整合nacos,使用nacos做注册和配置中心
    简单读读源码
    mybatis-plus.global-config.db-config.id-type=auto 和 @TableId(value = "id", type = IdType.ASSIGN_ID)哪个优先生效
  • 原文地址:https://www.cnblogs.com/Mufasa/p/11138801.html
Copyright © 2020-2023  润新知