Thread的基本用法

目录

1.创建线程

2.1 继承Thread类,重写run方法

2.2 实现Runnable,重写run方法

2.3 使用匿名内部类继承Thread类,重写run方法

2.4 使用匿名内部类实现Runnable接口,重写run方法

2.5 使用lambda表达式

3.Thread类的构造方法

4.Thread类的常见属性的获取方法

5.线程方法的使用

5.1 前台线程与后台线程

5.2 线程的执行顺序

5.3 线程休眠

5.4 线程中断

5.5 线程等待

5.6 获取线程实例


1.创建线程

在我们没有使用多线程编程之前,我们代码的运行全部都由main线程(main线程也是主线程)独自运行,而使用多线程的编程之后,我们的代码可以在多个线程上"同步"运行,下面先介绍创建线程的5种方法

2.1 继承Thread类,重写run方法

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

public class ThreadDemo1 {

    public static void main(String[] args){
        Thread t = new MyThread();

        t.start();
        while(true) {
            System.out.println("hello main");
        }
    }
}

首先创建一个类,由这个类去继承Thread类,重写其中的run方法

run方法: 创建出来的线程去执行的内容

start方法:  创建出一个新的线程,并让其去执行run方法里面的内容

执行上面的代码,会出现hello Thread 和 hello main交替打印的情况

他们的打印杂乱无章,可能是t线程(创建出来的线程)先打印,也可能是main线程先打印

这是由于: 当执行完t.start()之后,创建出来一个新的线程(简称t线程),t线程和main线程同属于在同一个进程上,他们在操作系统内核中的调度情况可能是并行,也可能是并发,是无法预知的,所以打印的前后顺序也是无法预知的.

sleep方法: sleep方法是Thread类中的一个静态方法,它可以让线程进入短暂休息的状态,比如sleep(1000) 就是休息1秒.(注意:sleep方法需要处理异常)

有了sleep方法,就可以更直观的去观察线程的输出情况,如下:

class MyThread extends Thread{
    @Override
    public void run() {
        while(true) {
            System.out.println("hello Thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadDemo1 {

    public static void main(String[] args){
        //创建一个Thread对象,new MyThread()
        Thread t = new MyThread();

        t.start();
        while(true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.2 实现Runnable,重写run方法

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("hello Thread");
    }
}


public class ThreadDemo2 {
    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Thread t = new Thread(runnable);
        t.start();
    }
}

首先创建一个类,实现Runnable接口,重写里面的run方法.

将Runnable对象传到Thread的构造方法中,然后t.start()线程就创建好了.

2.3 使用匿名内部类继承Thread类,重写run方法

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

和2.1 类似,只不过使用了匿名内部类.

2.4 使用匿名内部类实现Runnable接口,重写run方法

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

和 2.2 类似,将类实现替换成内部类实现

2.5 使用lambda表达式

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("hello Thread");
        });
        t.start();
    }
}

仿照代码中去写就好了,大括号里面的内容就相当于重写run方法的内容

3.Thread类的构造方法

方法1:Thread()

创建线程对象

方法2:Thread(Runnable r)

使用Runnable对象创建线程对象

方法3:Thread(String name)

创建线程对象,并初始化线程的名字

方法4:Thread(Runnable r,String name)

使用Runnalbe对象创建线程,并初始化线程的名字

4.Thread类的常见属性的获取方法

(1) getId()

获取线程的ID

(2) getName()

获取线程的名字

(3) getState()

获取线程的状态

(4) getPriority()

获取线程的优先级

(5) isDaemon()

判断线程是否为后台线程(true/false)

(6) isAlive()

判断线程是否存活(true/false)

(7) isInterrupted()

判断线程是否被中断(true/false)

5.线程方法的使用

5.1 前台线程与后台线程

前台线程: 会阻止进程结束,如果前台线程没有执行完,进程是无法结束的.

后台进程: 不会阻止进程结束,即使后台线程没有执行完,进程也是可以结束的.

我们在main方法中创建的线程会默认为前台线程,但是我们可以使用setDaemon()方法去设置线程

如下面的代码:

当执行完t.setDaemon(true) 之后,t线程就被设置成了后台线程,此时当主线程(main线程)执行完之后,t线程也就结束了.

因为当main线程执行晚最后一条语句时,它的任务就完成了,main线程会自动销毁(t线程如果执行完run方法的内容后也会自动销毁),而此时这个进程中只有main一个前台线程,当main线程被销毁后,其他的后台线程也就结束了.

public class ThreadDemo6 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("hello Thread");
                }
            }
        }, "MyThread");
        t.setDaemon(true);
        t.start();
    }
}

5.2 线程的执行顺序

线程的执行顺序是不确定的,如下面这个代码:

public class ThreadDemo7 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            for(int i = 0;i < 3;i++) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();

        for(int i = 0;i < 3;i++) {
            try {
                Thread.sleep(1000);
                System.out.println("hello main");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

当执行完 t.start() 之后,t线程被创建,此时t线程执行run方法中的内容,同时main线程继续执行main方法中下面的内容,此时,main线程遇到sleep(),休眠1s,所以会先打印 "hello Thread" ,而之后会先打印谁,后打印谁是不确定的.

因为在微观上,操作系统内核在调度两个线程时是使用并行还是并发是不确定的,也就导致了打印的顺序是不确定的.

5.3 线程休眠

线程的休眠和前面提到的sleep方法有关

当线程处于sleep状态时,就是处于休眠状态

如下:

public class Test {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while(true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

当前这个t线程就是处于休眠状态.

5.4 线程中断

线程中断相当于线程终止(结束),这个操作完全取决于设计线程里面的代码,我们给出的中断信息,只是传递了一个让其中断的信息,至于是否会真的被中断、被什么时候中断都是不确定的.

而传递中断信息可以有两种方法:

第一种:使用自己设置的标志位去传递中断信息

public class ThreadDemo8 {
    private static boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        Thread t= new Thread(() -> {

            while(flag) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();

        Thread.sleep(3000);
        flag = false;
    }
}

我们设置了一个boolean类型的静态成员变量 flag,让它成为我们的标志位,当我们将flag设置为false时,程序就会停止,至于会迅速停止的原因也是因为对run方法的设计.

第二种:使用Thread类自带的标志位

public class ThreadDemo9 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //等待0.5s在进行终止
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ex) {
                        e.printStackTrace();
                    }
                    break;
                }
            }
        });

        t.start();
        Thread.sleep(3000);
        t.interrupt();
    }
}

Thread类中有一个方法是Thread.currentThread().isIntercurrpted(),它的返回值默认为false

当外部调用t.interrupt()时 它的返回值会转变为true.

但是如果调用t.interript()方法时,t进程处于休眠状态(sleep状态),会立即被唤醒,被唤醒之后sleep会报出异常,但是代码仍然会继续执行.

此时sleep会进行清除标志位的操作,也就是将修改为true的标志位,修改回false

那么,为什么sleep要进行清除标志位的操作呢?

因为当程序被唤醒后,它不清楚程序是否要进行中断操作,将这个问题抛给了程序员,此时线程是否中断,完全取决于程序员设计的代码.

比如上面的事例代码,我在里面加入了break操作,让循环终止,程序结束,那么线程也就随之销毁,是执行了中断操作;而我又在break前面加上了一个sleep,让其先休眠 0.5s 再进行中断操作,这也就对应上了我最后开始说的:线程是否被中断,取决于其中代码的实现.

5.5 线程等待

线程等待使用的操作是Thread中的join()方法,它用类的实例来调用.

当执行t.join()方法后,当前执行的线程就会进入等待(也成为线程阻塞),等到t线程执行完,才可以继续执行当前的线程.

如下代码:

public class ThreadDemo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            for(int i = 0;i < 3;i++) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t.start();

        //如果在join之前t线程也就结束了,join就不会再阻塞,会立即返回
        //Thread.sleep(5000);


        System.out.println("join 之前");

        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("join 之后");
    }
}

上面的代码的执行顺序是如下这样:

join 之前

...(t线程的三遍 hello Thread 打印)

join 之后

也就是main线程等待t线程,当t线程执行完毕,join()才让main线程停止等待

而如果在执行到 t.join() 之前join线程就执行完了,那么main线程就不会等待.

比如:如果将上面代码注释的sleep(5000)打开,那么执行的结果就会变成下面的情况:

...(t线程的三遍 hello Thread 打印)

join 之前

join 之后

5.6 获取线程实例

Thread.currentThread()方法

它是一个类方法,作用是获取当前线程的实例

也就是说: 你在哪个线程中去调用这个方法,那么它就会返回这个线程的实例对象.

public class ThreadDemo11 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println(Thread.currentThread().getName());
        },"t线程");
        t.start();
    }
}

此时上面的代码输出为 -- t线程

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追梦不止~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值