线程| Thread类的基本用法

1. 线程创建

首先需要知道 需要通过继承Thread类来重写thread类的run方法, 并且查看Thread 类源码发现 该Thread 类继承了Runnable 而核心的run()方法正是Runnable 类的 因此衍生出以下创建线程的方法;

  • 通过创建Thread 的子类实现

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("继承Thread 实现 创建线程");
    }
}

再通过向上转型 接受Thead的子类并调用start方法

public static void main(String[] args) {
    //void 1:
    Thread t = new MyThread();
    t.start();
}

此时就有人要问了, 为什么不直接调用run方法而是调用start方法?

我们需要先知道run方法和start方法的区别;

  1. run方法只是我们重写Thread类的一个普通方法 , 如果直接使用 只相当于调用了t线程的一次run方法,并没有创建一个线程

  1. start方法里面 将run方法作为一个任务,并创建了一个线程让该线程执行这个任务 , 因此 该线程创建完之后相当于与主线程(main)是并发执行的.

  • 通过创建Thread 的匿名内部类

public static void main(String[] args) {
    Thread t3 = new Thread(){
        @Override
        public void run() {
            System.out.println("匿名内部类继承 Thread 创建线程");
        }
    };
    t3.start();
}
  • 通过创建Runnable接口的实现类

class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("实现Runnable 创建线程 ");
    }
}

与Thread创建子类同理, 向上转型后直接调用start()方法;

public static void main(String[] args) {
    //void 2:
    Thread t2 = new Thread(new MyRunnable());
    t2.start();
}
  • 通过创建Runnbale 接口的匿名内部类

public static void main(String[] args) {
   Thread t4 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("匿名内部类 实现Runnable 创建线程");
        }
    });
    t4.start();
}
  • 由第四个方法衍生lambda表达式写法

public static void main(String[] args) {
    Thread t5 = new Thread(() -> {
        System.out.println("lambda 表达式 实现Runnable接口 创建线程");
    });
    t5.start();
}

此时就有人会看出问题, Thread 和 Runnable 都差不多 , 我到底应该选择哪个去创建线程?

这里我推荐使用Runnable来创建线程, 因为Java中是单继承,如果继承了Thread 就受到了局限性,

如果去实现Runnable接口 就没有该问题.

2. 线程中断

在线程中 存在判断是否为中断的方法为Thread.currentThread.isInterrupted( );

线程的中断方法为 interrupt( );

public static void main(String[] args) {
    Thread.interrupted();//静态方法
 // 实例方法,其中currentThread 能够获取当前线程的实例
    Thread.currentThread().isInterrupted();
    Thread t2 = new Thread(() -> {
        //判断是否被设置标志位置了
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("hello thread2");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                //这里就是被打断了之后立就不执行了
                System.out.println("收尾工作...");
                break;
            }
        }
    });
    t2.start();
    //在主线程中,调用interrupt方法,来中断这个线程
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    //谁调用这个方法 的意思就是让线程 中断!
    //调用这个方法 可能会出现两种情况:
    //1) 如果t线程是处在就绪状态,就是设置线程标志位为true
    //2) 如果t是一个阻塞状态 Sleep 那么就会触发一个InterrputException
    t2.interrupt();
    System.out.println("终止 t2 线程!");
}

以上程序会捕捉到一个中断异常, 因为 sleep状态下 线程被打断会抛出一个打断异常. 因此我们捕捉到中断异常就意味着线程中断.

这里是因为如果不加sleep休眠的话,线程会执行非常的快 , 很难看出中断结果, 实际上当线程被中断后, Thread.currentThread().isInterrupted(); 标志位就会变成true 从而中断线程 而不是 通过捕捉中断异常而判断中断线程.

3. 线程等待

Thread.join();方法有几个版本

  • 无参数版本

相当于哪个线程调用这个方法,该线程要等待该线程一直到该线程结束,才继续往下执行操作.

public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
        for (int i = 0 ; i < 3; i++) {
            System.out.println("等等我 , 工作完找你!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t.start();
    //mian线程等待
    t.join();
    System.out.println("终于等到了,快点结束吧!");
    System.out.println("main线程结束");
}

  • 有参数版本

指定等待时间, 如果没有等到那就不再等待,继续执行下面的代码.

public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
        for (int i = 0 ; i < 3; i++) {
            System.out.println("等等我 , 工作完找你!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t.start();
    //mian线程等待 2秒后 如果没结束就直接自己结束!
    t.join(2000);
    System.out.println("来不及了,不等了!");
    System.out.println("main线程结束");
}

4. 线程休眠

Thread.sleep 静态方法

前面已经使用过sleep的用法 , 当一个线程调用Thread.sleep,该线程就会休眠指定的秒数

例如:让线程休眠五次

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        for (int i = 0; i < 5; i++) {
            try {
                System.out.println("线程进入休眠" + i + "次");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t.start();
}

5. 获取线程实例

通过使用Thred.currentThread.getName方法获取当前线程的名字

public static void main(String[] args) {
    Thread t = new Thread("线程1"){
        @Override
        public void run() {
            //获取thread 的实例对象的名字
            System.out.println(Thread.currentThread().getName());
            System.out.println(this.getName());
        }
    };
    t.start();
}

此时发现 通过this.getName 调用该类的获取名字的方法 和 获取当前线程名字的方法 Thred.currentThread.getName 是同样的效果 , 那么两者之间有什么区别呢?

this.getName 和 Thred.currentThread.getName 两者的区别是:

如果是Thread类的子类或者匿名内部类的话

this.getName 是可以获取到名字的.

如果是Runnable类型的, 就无法获取到名字,

因为Runnable接口没有getName方法 ,此时this 找不到getName方法 所以就会报红

因此, 当我们获取线程的实例名字的时候, 最好还是使用Thread.currentThread.getName方法;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值