Thread 类及常见方法

引言:
        Thread 类是 JVM ⽤来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关联。每个执⾏流,也需要有⼀个对象来描述,⽽ Thread 类的对象 就是⽤来描述⼀个线程执⾏流的,JVM 会将这些 Thread 对象组织起来,⽤于线程调度,线程管理。

 

一、Thread类常见构造方法

  1. Thread()
    Thread thread = new Thread();
    

    这将创建一个新的线程对象,但是不会执行任何任务。通常需要调用 start() 方法来启动线程,并通过覆盖 run() 方法或者传递 Runnable 接口的实现类来指定任务。

  2. Thread(Runnable target)
    Runnable task = () -> {
        // 执行线程任务
    };
    Thread thread = new Thread(task);
    

    这将创建一个新的线程对象,并将 Runnable 接口的实现类作为参数传递进去。线程启动后会执行传入的任务。

  3. Thread(Runnable target, String name)
    Runnable task = () -> {
        // 执行线程任务
    };
    Thread thread = new Thread(task, "Thread-1");
    

    这个构造方法允许为线程指定一个名字。这对于调试和识别线程非常有用。

  4. Thread(String name)
    Thread thread = new Thread("Thread-1");
    

    这将创建一个新的线程对象,并指定线程的名字,但是这个线程并没有关联任何任务。通常需要覆盖 run() 方法或者使用 start() 方法来启动线程。

  5. Thread(ThreadGroup group, Runnable target)
    ThreadGroup group = new ThreadGroup("MyThreadGroup");
    Runnable task = () -> {
        // 执行线程任务
    };
    Thread thread = new Thread(group, task);
    

    这个构造方法允许将线程放置在指定的 ThreadGroup 中,并指定一个 Runnable 任务。

  6. Thread(ThreadGroup group, Runnable target, String name)
    ThreadGroup group = new ThreadGroup("MyThreadGroup");
    Runnable task = () -> {
        // 执行线程任务
    };
    Thread thread = new Thread(group, task, "Thread-1");
    

    这个构造方法与前一个类似,但是允许同时指定线程的名字。

  7. Thread(ThreadGroup group, String name)
    ThreadGroup group = new ThreadGroup("MyThreadGroup");
    Thread thread = new Thread(group, "Thread-1");
    

    这个构造方法创建一个新的线程对象,并将其放置在指定的 ThreadGroup 中,但是不指定任何任务。

  8. Thread(ThreadGroup group, Runnable target, String name, long stackSize)
    ThreadGroup group = new ThreadGroup("MyThreadGroup");
    Runnable task = () -> {
        // 执行线程任务
    };
    Thread thread = new Thread(group, task, "Thread-1", 1024 * 1024);
    

    这个构造方法除了指定了 ThreadGroup、任务和名字之外,还允许指定线程堆栈的大小。堆栈大小是指线程的堆栈空间大小。

 

二、Thread 的几个常见属性 

 

  • ID 是线程的唯⼀标识,不同线程不会重复
  • 名称是各种调试⼯具⽤到
  • 状态表⽰线程当前所处的⼀个情况,下⾯我们会进⼀步说明
  • 优先级⾼的线程理论上来说更容易被调度到
  • 关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
  • 是否存活,即简单的理解,为 run ⽅法是否运⾏结束了
  • 线程的中断问题,下⾯我们进⼀步说明

 示例:

public class ThreadDemo {
        public static void main(String[] args) {
            Thread thread = new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    try {
                        System.out.println(Thread.currentThread().getName() + ": 我还活着");
                                Thread.sleep(1 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + ": 我即将死去");
            });
            System.out.println(Thread.currentThread().getName()
                    + ": ID: " + thread.getId());
            System.out.println(Thread.currentThread().getName()
                    + ": 名称: " + thread.getName());
            System.out.println(Thread.currentThread().getName()
                    + ": 状态: " + thread.getState());
            System.out.println(Thread.currentThread().getName()+ ": 优先级: " + thread.getPriority());
            System.out.println(Thread.currentThread().getName()
                    + ": 后台线程: " + thread.isDaemon());
            System.out.println(Thread.currentThread().getName()
                    + ": 活着: " + thread.isAlive());
            System.out.println(Thread.currentThread().getName()
                    + ": 被中断: " + thread.isInterrupted());
            thread.start();
            while (thread.isAlive()) {}
            System.out.println(Thread.currentThread().getName()
                    + ": 状态: " + thread.getState());
        }
    }

 

三、启动⼀个线程 - start() 

        之前我们已经看到了如何通过覆写 run ⽅法创建⼀个线程对象,但线程对象被创建出来并不意味着线程就开始运⾏了。
  • 覆写 run ⽅法是提供给线程要做的事情的指令清单
  • 线程对象可以认为是把 李四、王五叫过来了
  • ⽽调⽤ start() ⽅法,就是喊⼀声:”⾏动起来!“,线程才真正独⽴去执⾏了。

 

调⽤ start ⽅法, 才真的在操作系统的底层创建出⼀个线程 。

 

四、中断⼀个线程 

        李四⼀旦进到⼯作状态,他就会按照⾏动指南上的步骤去进⾏⼯作,不完成是不会结束的。但有时我们需要增加⼀些机制,例如⽼板突然来电话了,说转账的对⽅是个骗⼦,需要赶紧停⽌转账,那张三该如何通知李四停⽌呢?这就涉及到我们的停⽌线程的⽅式了。
⽬前常⻅的有以下两种⽅式:
  1. 通过共享的标记来进⾏沟通
  2. 调⽤ interrupt() ⽅法来通知
示例1:使⽤⾃定义的变量来作为标志位 ,需要给标志位上加 volatile 关键字
public class ThreadDemo2 {
        private static class MyRunnable implements Runnable {
            public volatile boolean isQuit = false;
            @Override
            public void run() {
                while (!isQuit) {
                    System.out.println(Thread.currentThread().getName()
                            + ": 别管我,我忙着转账呢!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()
                        + ": 啊!险些误了⼤事");
            }
        }
        public static void main(String[] args) throws InterruptedException {
            MyRunnable target = new MyRunnable();
            Thread thread = new Thread(target, "李四");
            System.out.println(Thread.currentThread().getName()
                    + ": 让李四开始转账。");
            thread.start();
            Thread.sleep(3 * 1000);
            System.out.println(Thread.currentThread().getName()
                    + ": ⽼板来电话了,得赶紧通知李四对⽅是个骗⼦!");
            target.isQuit = true;
        }
    }

 

示例2:使⽤ Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替⾃定义标志位(Thread 内部包含了⼀个 boolean 类型的变量作为线程是否被中断的标记)
public class ThreadDemo3 {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            // 两种⽅法均可以
            while (!Thread.interrupted()) {
                //while (!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName()
                            + ": 有内⻤,终⽌交易!");
                    // 注意此处的 break
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了⼤事");
        }
        public static void main(String[] args) throws InterruptedException {
            MyRunnable target = new MyRunnable();
            Thread thread = new Thread(target, "李四");
            System.out.println(Thread.currentThread().getName()
                    + ": 让李四开始转账。");
            thread.start();
            Thread.sleep(3 * 1000);
            System.out.println(Thread.currentThread().getName()
                    + ": ⽼板来电话了,得赶紧通知李四对⽅是个骗⼦!");
            thread.interrupt();
        }
    }
}

thread 收到通知的⽅式有两种:
  1. 如果线程因为调⽤ wait/join/sleep 等⽅法⽽阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程。
  2. 否则,只是内部的⼀个中断标志被设置,thread 可以通过 Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志这种⽅式通知收到的更及时,即使线程正在 sleep 也可以⻢上收到。

 

 五、等待⼀个线程 - join()

        有时,我们需要等待⼀个线程完成它的⼯作后,才能进⾏⾃⼰的下⼀步⼯作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要⼀个⽅法明确等待线程的结束。
public class ThreadDemo4 {
    public static void main(String[] args) throws InterruptedException {
        Runnable target = () -> {
            for (int i = 0; i < 3; i++) {
                try {
                    System.out.println(Thread.currentThread().getName()
                            + ": 我还在⼯作!");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ": 我结束了!");
        };
        Thread thread1 = new Thread(target, "李四");
        Thread thread2 = new Thread(target, "王五");
        System.out.println("先让李四开始⼯作");
        thread1.start();
        thread1.join();
        System.out.println("李四⼯作结束了,让王五开始⼯作");
        thread2.start();
        thread2.join();
        System.out.println("王五⼯作结束了");
        }
}

 

六、获取当前线程引用

public class ThreadDemo {
 public static void main(String[] args) {
         Thread thread = Thread.currentThread();
         System.out.println(thread.getName());
         }
 }

七、休眠当前线程  

        有⼀点要记得,因为线程的调度是不可控的,所以,这个⽅法只能保证实际休眠时间是⼤于等于参数设置的休眠时间的
public static void sleep(long millis) throws InterruptedException

其中,millis 是线程要睡眠的时间,以毫秒为单位。 

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3 * 1000);
        System.out.println(System.currentTimeMillis());
    }
}

注意事项

  • sleep() 方法不会释放线程所拥有的锁,因此其他线程无法获得被 sleep() 方法占用的锁,直到当前线程的睡眠时间到期。
  • sleep() 方法是静态方法,因此调用时应该使用 Thread.sleep(),而不是在一个实例上调用。
  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值