多线程 02

简介: 多线程 02

1.线程的常见构造方法

方法 说明
Thread() 创建线程对象
Thread(Runnable target) 使用 Runnable 对象创建线程对象
Thread(String name) 创建线程对象,并命名
Thread(Runnable target, String name) 使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group,
Runnable target)
线程可以被用来分组管理,分好的组即为线程组,这
个目前我们了解即可

2.线程的几个属性和方法

属性 获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()

这里的后台线程和前台线程不同,当所有的前台线程执行完毕,即使后台线程还在工作中,也会直接自动退出.

只要前台线程没执行完,进程就不会结束,即使main结束了,前台线程也会继续执行

设置为后台进程

setDaemon()设为true就是后台,不设置就是默认前台

isAlive()表示内核中的PCB是否存在

这个对象的生命周期和PCB的是不完全一样的

因为我在创建对象之后这个PCB才存在,在执行完进程结束后之后这个PCB才销毁,而且java中的用户级线程不是直接映射到操作系统中的原生线程的

3.创建线程的5个方式

其实线程是操作系统提供的一种机制,并给用户提供了一些api供使用,Java的Thread类只是对其的进一步封装.

1.继承Thread类,重写run方法

2.重写runnable接口

3.使用匿名内部类实现Thread

4.使用匿名内部类实现Runnable接口

5.使用Lambda表达式

4.start()和run()方法的区别

说实话这两个方法是八竿子打不着的,为什么要把他们两个放进来比较呢?

可能有人认为start方法和run方法执行的是一件事情

其实这个理解是大错特错的,这两个方法完全不一样,我们举个例子

假设我们在调用main方法中调用一个run方法,你可能以为这样和使用start方法是一样的,其实不然,我们在run方法中是一个死循环,在main方法中调用run方法,下面再写一个死循环,此时控制台只会打印线程中的死循环的内容而不会打印main方法中的内容

而start方法实际上创建了一个新的线程,根据cpu的随机调度(抢占式调度),两者都可能打印出来,下面我给出代码支持.

package Test;
import java.awt.desktop.ScreenSleepEvent;
public class ThreadDemo2 {
    public static void main(String[] args) {
        Thread t = new  Thread(()->{
            while (true){
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.run();
        while (true){
            System.out.println("Hello main");
        }
    }
}

package Test;
import java.awt.desktop.ScreenSleepEvent;
public class ThreadDemo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new  Thread(()->{
            while (true){
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        while (true){
            System.out.println("Hello main");
            Thread.sleep(1000);
        }
    }
}

5.终止一个线程

package Thread;
public class ThreadDemo12 {
    private static boolean isQuit = false;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(!isQuit){
                System.out.println("我是一个线程,工作中");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("线程工作完毕");
        });
        t.start();
        Thread.sleep(3000);
        isQuit = true;
        System.out.println("让t线程退出");
    }
}

最后结果为

这就好比这个时候我在打游戏,女朋友突然叫我去和她一起开心一下,这个时候就是main线程来中断我这个t线程,我得配合她她才能中断我的动作,假设我不配合,那她也没有办法.

所以这里的线程运行中想要中断,其实是需要两个线程中相互配合的,这里就使用了isQuit变量来实现其中的相互配合.

.

其实这里也有一个方法来实现对线程的中断,我们来尝试一下

interrupt()

isInterrupted() 判断是否被打断

我们不妨来试试用这个来作为标志位

package Thread;
public class ThreadDemo13 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("我是一个线程,正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行完毕");
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("t线程退出");
        t.interrupt();
    }
}

结果是

我们发现t线程并没有真正的执行结束,并且捕获了一个打断异常

结论:

sleep中的线程被打断之后,线程并没有直接退出,而是继续一直输出信息

这是因为线程在睡眠中被打断会抛出一个异常,并将isInterrupted重新设置为false

解决方案,在收到这个打断的异常之后,直接break跳出循环

有人会觉得还不如之前的isQuit标志位好,想在遇到这个异常信息的时候将isQuit设置为true,其实是行不通的,可以参考我上一篇文章中的变量捕获.因为匿名内部类中调用的局部变量只能是final修饰的或是事实final的

6.线程中的join方法

join方法能够实现线程的等待,假如在main线程中调用t.join,此时main线程就会等待t线程执行结束后再继续工作,t线程执行的时候,此线程属于阻塞状态

下面举个例子说明一下

package Thread;
public class ThreadDemo14 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("马上到...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("终于到了!!!");
        });
        t1.start();
        t1.join();
        System.out.println("我等你好久了~~~");
    }
}

执行结果如下

注:start方法一定在join方法之前调用

此时main线程等待了t1线程执行完了才开始执行

相关文章
|
Java API 调度
|
安全 Java
多线程02
多线程02
|
6月前
|
设计模式 安全 Java
多线程问题(三)
多线程问题(三)
35 0
|
6月前
|
安全 Java C#
C#多线程详解
C#多线程详解
57 0
|
6月前
|
缓存 安全 Java
多线程05
多线程05
28 0
|
6月前
|
前端开发 Java
|
11月前
|
安全 Linux C++
C++多线程
C++多线程
53 1
|
监控 Java API
多线程专题
多线程专题
|
API
多线程具体实现(下)
多线程具体实现
48 0