JAVAEE之Thread类详解

一.线程创建

1)继承Thread类

创建一个类,同时继承自标准库的Thread类,在类中重写run方法。

然后,在main函数中实列化刚才创建的类,并通过实例.start()创建并运行线程。

例:

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("Hello Thread");
    }
}

public class Demo1 {

    public static void main(String[] args) {
        MyThread t1=new MyThread();
        t1.start();
    }
}

注意!!上述代码中run方法并没有手动调用,但最终也执行了。像这样用户手动定义,却没有手动调用,最终被系统/库/框架进行调用的方法被称为“回调函数”(callback)。

2)实现Runnable接口

创建一个类,实现Runnable接口,在里面重写run方法。

在main函数中,实列化创建的类,并将实列填入Thread()中。

例:

class MyRunnable implements Runnable{

    @Override
    public void run() {
        System.out.println("Hello Thread");
    }
}
public class Demo2 {
    public static void main(String[] args) {
        MyRunnable myRunnable=new MyRunnable();
        Thread t1=new Thread(myRunnable);
        t1.start();


    }
}

其中,Runnable就是用来描述要执行的任务是什么。

线程要执行的任务通过Runnable来描述,而不是通过Thread自己来描述。

通过Runnable,有利于代码解耦合。

3)通过匿名内部类来实现

public class Demo3 {
    public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run() {
                System.out.println("Hello Thread");
            }
        };

        t.start();
    }
}

匿名内部类,就是“一次性使用的类”,用完就丢。

4)使用lambda创建线程

public class Demo4 {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            System.out.println("Hello Thread");
        });

        t1.start();
    }
}

本质上还是匿名内部类,不过更为简化。

二.线程的启动

启动线程一般用start()方法即可

 public static void main(String[] args) {
        Thread t1=new Thread();
        t1.start();
    }

注:start与run的区别

作用功能不同:

  1. run方法的作用是描述线程具体要执行的任务;
  2. start方法的作用是真正的去申请系统线程

运行结果不同:

  1. run方法是一个类中的普通方法,主动调用和调用普通方法一样,会顺序执行一次;
  2. start调用方法后, start方法内部会调用Java 本地方法(封装了对系统底层的调用)真正的启动线程,并执行run方法中的代码,run 方法执行完成后线程进入销毁阶段。

三.获取当前线程引用

currentThread()是Thread类的静态方法,能获取到调用这个方法的实列。

public class Demo2 {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            System.out.println("t1线程名:"+Thread.currentThread().getName());
        });
        t1.start();
        System.out.println("main线程:"+Thread.currentThread().getName());
    }
}

四.中断线程

线程中断指让一个线程结束。

列如有两个线程A和B,B在运行,A想让B结束,核心就是A想办法让B的run方法执行完毕,而不是当B运行一半,A强制让其结束。

JAVA中,结束线程是一个"温柔"的过程,而不是直接粗暴的。

以下为中断线程的一种方法:

public class Demo3 {
    private static boolean isQuite=false;
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            while (!isQuite){
                System.out.println("Hello t1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("线程t1结束");
        });
        t1.start();
        Thread.sleep(3000);
        System.out.println("main线程尝试结束t1线程");
        //修改变量,实现中断线程
        isQuite=true;
    }
}

Thread类里有个成员变量interruptued,bool类型,初始值为false

通过使用isInterrupted()方法就会设置以上标准位 

public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
           Thread currentThread=Thread.currentThread();
           while (!currentThread.isInterrupted()){
               System.out.println("Hello t1");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException();
               }
           }
        });
        t1.start();
        Thread.sleep(3000);
        t1.interrupt();
    }
}
//运行时报错

运行时发现,代码抛出异常。

由于判断isInterrupted()和执行打印时间太快,整个循环大多数时间都花在sleep上。

main函数调用interrupt时,t1线程大概率在sleep状态

此时Interrupt不仅能设置标准位,还会唤醒sleep操作(会抛出异常 )

注:当sleep被唤醒后,会清空刚才设置的interrupted标准位

因此,我们可以在catch中调整代码,取消抛出异常,并添加break结束循环。

public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
           Thread currentThread=Thread.currentThread();
           while (!currentThread.isInterrupted()){
               System.out.println("Hello t1");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   System.out.println("调整catch部分");
                   break;   //退出循环
               }
           }
            System.out.println("t1线程结束");
        });
        t1.start();
        Thread.sleep(3000);
        t1.interrupt();
    }
}

上述规则,可以让程序员有更多操作空间。

五.线程的属性与获取方法

1)获取线程的ID

可通过getID()方法获得线程的ID

 public class Demo1 {
        public static void main(String[] args) {
            Thread t1=new Thread(()->{

            });
            t1.start();
            System.out.println("线程t1id为:"+t1.getId());
            System.out.println("main线程id为"+Thread.currentThread().getId());

        }
    }

运行结果:

2)获取线程的名称

通过getName()方法获取线程的名字

public class Demo1 {
        public static void main(String[] args) {
            Thread t1=new Thread(()->{

            });
            t1.start();
            System.out.println("t1线程名称:"+t1.getName());

        }
    }

如不主动定义,线程名字默认为Thread-0,我们可以在线程里命名线程。

public class Demo1 {
        public static void main(String[] args) {
            Thread t1=new Thread(()->{

            },"自定义名字");
            t1.start();
            System.out.println("t1线程名称:"+t1.getName());

        }
    }

3)获得线程的状态

通过getState()方法获取线程的状态

  public class Demo1 {
        public static void main(String[] args) {
            Thread t1=new Thread(()->{
                
            });
            t1.start();
            System.out.println("t1线程状态:"+t1.getState());

        }
    }

        

在JAVA中,线程状态分为六种

  1. 新建(NEW):创建未被使用的线程处于这种状态。

     public class Demo1 {
            public static void main(String[] args) {
                Thread t1=new Thread(()->{
    
                });
                //t1线程没有start()创建   
                System.out.println("t1线程状态:"+t1.getState());
    
            }
        }

  2. 运行(RUNNABLE):调用start()后,线程就绪(没运行,但随时可以运行)或正在运行的状态都为RUNNABLE。
    public class Demo1 {
            public static void main(String[] args) {
                Thread t1=new Thread(()->{
    
                });
                t1.start();
                System.out.println("t1线程状态:"+t1.getState());
    
            }
        }

  3. 无限期等待(WAITING):线程不会被分配cpu执行时间,无限的等待。使用无参数的join()时可能会进入此状态。
     public class Demo1 {
            public static void main(String[] args) throws InterruptedException {
                Thread t1=new Thread(()->{
                while (true){
    
                }
                });
                Thread t2=new Thread(()->{
                    try {
                        t1.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                t1.start();
                t2.start();
                Thread.sleep(2000);
                System.out.println("t2线程状态:"+t2.getState());
    
            }
        }
    

  4. 限期等待(TIMED_WAITING):处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒。(sleep()和带参数的join()可使线程进入此状态)
       public class Demo1 {
            public static void main(String[] args) throws InterruptedException {
                Thread t1=new Thread(()->{
                while (true){
    
                }
                });
                Thread t2=new Thread(()->{
                    try {
                        t1.join(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                t1.start();
                t2.start();
                Thread.sleep(2000);
                System.out.println("t2线程状态:"+t2.getState());
    
            }
        }

  5. 阻塞(BLOCKED):线程被阻塞了
  6. 结束(TERMINATED):已终止线程的线程状态,线程已经结束执行。

4)获取进程优先级

线程通过getPriority()方法获取线程优先级。

设置不同优先级会影响系统调度。

public class Demo1 {
        public static void main(String[] args) throws InterruptedException {
            Thread t1=new Thread(()->{

            });
            t1.start();
            System.out.println("线程t1优先级:"+t1.getPriority());
            System.out.println("main线程优先级:"+Thread.currentThread().getPriority());
        }
    }

5)获取线程是否为后台线程

何为前台线程与后台线程

再介绍方法前,先介绍何为前台线程与后台线程

前台线程:某个线程在运行过程中,能够阻止进程结束,为"前台线程"(main线程)。

后台线程(守护进程):线程在运行过程中,不能阻止进程结束,随着进程一起结束,为“后台线程”。

简单来说,如果当main线程结束后,进程还在进行,那该线程为前台线程

如以下代码

 public class Demo1 {
        public static void main(String[] args) throws InterruptedException {
            Thread t1=new Thread(()->{
            while(true){
                System.out.println("Hello Thread");
            }
            });
            t1.start();
            System.out.println("main线程结束");
        }
    }

main线程已经结束,t1线程却还在运行,此时t1为前台线程

当我们用setDaemon()将t1线程设为后台线程时,main线程结束将带走t1线程

 public class Demo1 {
        public static void main(String[] args) throws InterruptedException {
            Thread t1=new Thread(()->{
            while(true){
                System.out.println("Hello Thread");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            });
            //将t1设为后台进程
            t1.setDaemon(true);
            t1.start();

            System.out.println("main线程结束");
        }
    }

获取是否为后台线程

通过isDaemon()来获取线程是否为后台进程,若是返回true,否返回false

 public class Demo1 {
        public static void main(String[] args) throws InterruptedException {
            Thread t1=new Thread(()->{
            while(true){
                System.out.println("Hello t1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            });
            Thread t2=new Thread(()->{
                while(true){
                    System.out.println("Hello t2");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t1.setDaemon(true);
            t1.start();
            t2.start();
            System.out.println("t1线程是否为后台线程"+t1.isDaemon());
            System.out.println("t2线程是否为后台线程"+t2.isDaemon());

        }
    }

6)获取线程是否存活

代码中,创建的NEW Thread对象,生命周期和内核实际中的线程是不一定一样的。

可能会出现Thread对象仍然存在,但内核中的线程不存在这种情况(不会出现Thread对象不存在,但内核线程存在)。

通过isAlive()获取线程是否存活

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("Hello t1");
            }
        });
        System.out.println("t1线程创建前的存活状态"+t1.isAlive());
        t1.start();
        System.out.println("t1线程创建后的存活状态"+t1.isAlive());;
        Thread.sleep(4000);
        System.out.println("t1线程结束后的存活状态"+t1.isAlive());
    }
}

六.线程休眠

通过Thread.sleep()可以使线程休眠(阻塞)

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        long start=System.currentTimeMillis();  //获取线程开始时间
        Thread.sleep(2000);  //休眠2000ms
        long end=System.currentTimeMillis();
        System.out.println("线程运行时间:"+(end-start));

    }
}

   sleep 的原理就是让原本在 就绪队列 中的线程,转移到 阻塞队列 中去,等 sleep 的时间到了,再转移到 就绪队列 中。​

七.线程等待

多线程并发编程,一般情况下线程的调度顺序是不确定的。

通过join()可以干预线程的结束顺序

以下代码,主线程等待t1线程结束后再结束

    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("Hello t1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t1线程结束");
        });
        t1.start();
        System.out.println("主线程开始等待");
        t1.join();
        System.out.println("主线程结束等待");
    }

以上代码join不带参数,会等到特定线程结束后继续运行,为"死等"。

join还有带参数的版本,在()内填写参数,超过时间后,等待线程也会执行。


以上便是全部内容,若有不对,欢迎指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值