Java线程——常见方法

本文介绍了Java中线程的基本概念,包括进程、线程的定义与区别,以及线程的创建方法如继承Thread类和实现Runnable接口。详细阐述了Thread类的重要方法,如start()、join()、中断线程和线程状态,帮助理解Java线程的管理和调度。
摘要由CSDN通过智能技术生成

线程(Thread)

计算机的核心是CPU,它承担了计算机的所有计算任务,CPU就像一个工厂,时刻在运行着,而操作系统管理着计算机,负责任务的调度、资源的分配和管理。

概念:

什么是程序:
例如,我们的桌面上都会安装QQ、酷狗音乐、微信…等,这些就是程序。当我们点击QQ运行时,QQ正常运行,此时就会开启一个进程。

什么是进程:
进程指在系统中正在运行的一个应用程序;程序一旦运行就是进程。

程序是静态的,而进程是动态的,程序是作为进程的运行的载体,进程会随时间,会在某一时刻消亡。

什么是线程:
线程是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

进程——系统资源分配的最小单位;线程——CPU调度的最小单位;
一个进程内的线程之间是可以共享资源的;每个进程至少有一个线程存在,即主线程。

在这里插入图片描述
在这里插入图片描述

对比线程和进程

线程的优点:

1. 创建一个新线程的代价要比创建一个新进程小得多
2. 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
3. 线程占用的资源要比进程少很多
4. 能充分利用多处理器的可并行数量
5. 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
6. 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
7. I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

进程与线程的区别:

1. 进程是系统进行资源分配和调度的一个独立单位,线程是程序执行的最小单位。
2. 进程有自己的内存地址空间,线程只独享指令流执行的必要资源,如寄存器和栈。
3. 由于同一进程的各线程间共享内存和文件资源,可以不通过内核进行直接通信。
4. 线程的创建、切换及终止效率更高。

那为什么有进程还要多线程呢?
每个进程都有自己独自的代码和数据空间,即为「程序的上下文」,进程包含多个线程,进程的切换消耗要大于线程的切换消耗。
在这里插入图片描述
线程可以看作是轻量级的进程,每个线程也有自己的「运行栈」和「程序计数器(PC)」、以及「线程的本地存储」,所以对于进程数比较多的,频繁的切换进程将会带来一大笔的开销,反而线程的切换开销小。
在这里插入图片描述

创建线程

方法1:继承 Thread 类
通过继承 Thread 来创建一个线程类。
该方法的好处是 this 代表的就是当前线程,不需要通过 Thread.currentThread() 来获取当前线程的引用。

class MyThread extends Thread{
    @Override
    public void run(){
        System.out.println(this.getName());
    }
}
public class Demo {
    public static void main(String[] args) {
    	// 创建并启动线程
        MyThread t=new MyThread();
        t.start();   
    }
}

方法2:实现 Runnable 接口
通过实现 Runnable 接口,并且调用 Thread 的构造方法时将 Runnable 对象作为 target 参数传入来创建线程对象。
该方法的好处是可以规避类的单继承的限制;但需要通过 Thread.currentThread() 来获取当前线程的引用。

class MyRunnable implements Runnable{
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()
                +"这里是线程运行的代码");
    }
}
public class Demo1 {
    public static void main(String[] args) {
    	// 创建并启动线程
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable).start();    
    }
}

Thread 类及常见方法

Thread 类是 JVM 用来管理线程的一个类,即:每个线程都有一个唯一的 Thread 对象与之关联。
每个执行流,需要有一个对象来描述,类似下图所示,
而 Thread 类的对象就是用来描述 一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。
在这里插入图片描述

1. Thread 的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target,String name)使用 Runnable 对象创建线程对象,并命名
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable()); 
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2. Thread 的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
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());
    }
}

在这里插入图片描述

3. 启动一个线程-start()

线程对象被创建出来并不意味着线程就开始运行了

注意:run 方法和 start 方法是不同的,启动线程必须要调用 start 方法。

4.中断一个线程

通过共享的标记来进行沟通
调用 interrupt() 方法来通知

示例:

public class Demo2 {
    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;
                }
            }
            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(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        thread.interrupt();
    }
}
1. 通过 thread 对象调用 interrupt() 方法通知该线程停止运行
2. thread 收到通知的方式有两种:
        1.如果线程调用了 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,
          清除中断标志
        2.否则,只是内部的一个中断标志被设置,thread 可以通过
              1.Thread.interrupted()
                判断当前线程的中断标志被设置,清除中断标志
              2.Thread.currentThread().isInterrupted()
                判断指定线程的中断标志被设置,不清除中断标志

这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到

方法说明
public void interrupt()中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位
public static boolean interrupted()判断当前线程的中断标志位是否设置,调用后清除标志位
public boolean isInterrupted()判断对象关联的线程的标志位是否设置,调用后不清除标志位

5.等待一个线程-join()

等待一个线程完成它的工作后,才能进行自己的下一步工作。

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)同理,但可以更高精度
public class JoinDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable target = () -> {
            for (int i = 0; i < 10; 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("王五工作结束了");
    }
}

6. 获取当前线程引用

方法说明
public static Thread currentThread();返回当前线程对象的引用
    public static void main(String[] args) {
        Thread thread = Thread.currentThread(); 
        System.out.println(thread.getName());
    }

7. 休眠当前线程

因为线程的调度是不可控的,所以,这个方法只能保证休眠时间是大于等于休眠时间的。

方法说明
public static void sleep(long millis) throws InterruptedException休眠当前线程 millis 毫秒
public static void sleep(long millis, int nanos) throws InterruptedException可以更高精度的休眠
public static void main(String[] args) throws InterruptedException { 
    System.out.println(System.currentTimeMillis());
    Thread.sleep(3 * 1000);
    System.out.println(System.currentTimeMillis());
}   

状态

线程的状态

初始态,执行态,等待状态,就绪状态,终止状态
在这里插入图片描述

线程的状态

在这里插入图片描述
单线程时代,一次只能执行一个任务,后面的任务只能排队等候,实现的方式都是串行化的,随着后续的发展为了提高效率,实现了多线程。

单CPU的多线程方式,实现的是并发方式,并发真正意义上的并行,因为CPU一次只能执行一次任务,但是,CPU的执行速度远快于线程的执行速度,为了充分利用CPU,因此实现并发的多线程方式。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值