【Java并发】创建和使用线程常用方法总结

目录

什么是线程

线程和进程、协程(虚拟线程)的区别

线程和进程的区别

线程和协程的区别

线程的六种状态

1.NEW

2.RUNNABLE

3.BLOCKED

4.WAITING

5.TIME_WAITING

6.TERMINATED

创建线程

方法一:通过继承Thread类

方法二:通过实现Runnable接口

两种方法的区别

线程常用方法 

start 与 run 线程执行

sleep 线程休眠

wait 线程等待 

join 线程合并

interrupt 线程中断

yield 线程让步 

setPriority 线程优先级

setDaemon 设置守护线程


什么是线程

线程是操作系统中的基本执行单元(能够直接执行的最小代码块),它是进程中的⼀个实体,是CPU调度和分派的基本单位。

线程和进程、协程(虚拟线程)的区别

线程和进程的区别

  • 在操作系统中,进程是基本的资源分配单位,操作系统通过进程来管理计算机的资源,如CPU、内存、磁盘等。⼀个进程可以包含多个线程,每个线程都可以独立执行不同的任务,但它们共享进程的资源。

线程和协程的区别

  • 调度方式上,线程由操作系统调度,切换线程时会涉及上下文切换和内核态的开销;协程由程序调度,在用户态切换,没有上下文切换的开销,性能更高。

  • 阻塞与非阻塞方面,线程通常采用阻塞模型;协程是非阻塞的。

  • 资源占用方面,线程每个线程需要分配栈空间,且栈大小固定,导致线程资源消耗较大;协程的栈空间可以动态增长,内存开销远小于线程。

总结一下:先有进程,然后进程可以创建线程,线程是依附在进程里面的, 线程里面可以包含多个协程;进程之间不共享全局变量,线程之间共享全局变量,但是要注意资源竞争的问题。

线程的六种状态

1.NEW

初始状态,线程被构建,但是还没有调用start()方法。

2.RUNNABLE

运行状态,Java线程将操作系统中的就绪和运行两种状态统称为"运行中"。

3.BLOCKED

阻塞状态,表示线程阻塞于锁。

4.WAITING

等待状态,表示线程进入等待状态,进入该状态表示当前线程需要其他线程通知(notify或者notifyAll)。

5.TIME_WAITING

超时等待状态,可以指定等待时间自己返回。

6.TERMINATED

终止状态,表示当前线程已经执行完毕。

创建线程

方法一:通过继承Thread类

  • 创建一个类继承Thread类。

  • 重写该类的run()方法,将线程要执行的代码放在run()方法中。

  • 创建该类的实例,并调用其start()方法来启动线程。

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程开始运行");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start(); 
    }
}

方法二:通过实现Runnable接口

  • 创建一个类实现Runnable接口。

  • 实现Runnable接口的run()方法,将线程要执行的代码放在run()方法中。

  • 创建该类的实例,并将其作为参数传递给Thread类的构造函数,创建线程对象。

  • 调用线程对象的start()方法来启动线程。

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程开始运行");
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start(); 
    }
}

或者更简单的写为:

    public static void main(String args[]){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程开始运行");
            }
        });

        thread.start();
    }

两种方法的区别

  • 继承Thread类:简单直接,但Java不支持多继承,如果类已经继承了其他类,则无法再继承Thread类。

  • 实现Runnable接口:更灵活,推荐使用。可以避免Java单继承的限制,适合多个线程共享同一个资源的情况。

线程常用方法 

start 与 run 线程执行

start()run()方法都与线程的执行有关,但它们之间有着本质的区别:

  • 执行run方法它不会产生新线程,而执行start方法会产生新线程。

  • run方法可以被执行无数次,而star方法只能被执行⼀次,原因就在于线程不能被重复启动。

 总结一下:run方法的作用是存放任务代码,而start的方法呢是启动线程。

sleep 线程休眠

sleep()方法是Thread类的一个静态方法,用于让当前正在执行的线程暂停执行指定的时间,以便其他线程可以运行。

休眠线程例如:

public class SleepExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("线程开始执行");
                Thread.sleep(2000); // 睡2秒
                System.out.println("线程执行完毕");
            } catch (InterruptedException e) {
                System.out.println("线程被中断");
            }
        });
        thread.start();
    }
}
sleep 特点

不释放锁、对中断敏感、释放 CPU。

wait 线程等待 

wait()方法是Object类的一个方法,用于让当前线程等待,直到其他线程调用同一对象的notify()notifyAll()方法。

wait()方法通常用于实现线程间的协调和同步,常用于生产者-消费者模型,例如:

public class WaitNotifyExample {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        Thread producer = new Thread(() -> {
            synchronized (lock) {
                System.out.println("生产者开始生产");
                // 生产产品...
                System.out.println("生产者生产完毕");
                lock.notify(); // 通知消费者
            }
        });

        Thread consumer = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("消费者开始等待");
                    lock.wait(); // 等待生产者生产完毕
                    System.out.println("消费者开始消费");
                    // 消费产品...
                } catch (InterruptedException e) {
                    System.out.println("消费者被中断");
                }
            }
        });

        consumer.start();
        producer.start();
    }
}
wait 特点 

释放锁、对中断敏感、释放 CPU。

注意:wait()方法必须在同步块(synchronized block)或同步方法(synchronized method)中调用,否则会抛出IllegalMonitorStateException异常。因为wait()方法需要释放对象的锁,而只有在同步块中才能获取对象的锁。

join 线程合并

join()方法是Thread类的一个方法,用于等待一个线程终止。当你调用一个线程对象的join()方法时,当前线程会暂停执行,直到被调用join()方法的线程执行完毕。

常用于确保执行顺序例如:

public class JoinExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("线程1开始执行");
            // 模拟耗时操作
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1执行完毕");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("线程2开始执行");
            try {
                thread1.join(); // 等待线程1执行完毕
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2执行完毕");
        });

        thread1.start();
        thread2.start();
    }
}

interrupt 线程中断

interrupt()方法是Thread类的一个方法,用于中断一个线程。中断是一种协作机制,它不会立即停止线程,而是设置线程的中断状态,线程需要检查这个状态并作出相应的响应。

举个简单的例子:

public class InterruptExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                // 执行任务...
                System.out.println("线程正在运行");
                try {
                    Thread.sleep(1000); // 模拟耗时操作
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 重新设置中断状态
                    break; // 退出循环
                }
            }
            System.out.println("线程被中断,退出执行");
        });

        thread.start();
        try {
            Thread.sleep(3000); // 让主线程等待一段时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt(); // 中断线程
    }
}

注意:线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位。

yield 线程让步 

yield()方法是Thread类的一个静态方法,用于提示线程调度器当前线程愿意让出CPU的执行权,以便其他线程可以执行。

举个简单的例子:

public class YieldExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("线程1执行:" + i);
                if (i == 2) {
                    Thread.yield(); // 在i等于2时让出CPU执行权
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("线程2执行:" + i);
            }
        });

        thread1.start();
        thread2.start();
    }
}

注意:yield()方法并不能保证线程⼀定会让出CPU资源,它只是⼀个提示,告诉调度器当前线程愿意让出CPU资源。具体是否让出CPU资源,还是由调度器决定。 

setPriority 线程优先级

在Java线程中,通过一个整型成员变量priority来控制优先级,优先级的范围从1~10,在线程构建的时候可以通过setPriority(int)方法来修改优先级,默认优先级是5,优先级高的线程分配CPU时间片的数量要多于优先级低的线程。

修改线程优先级例如:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("线程正在运行");
    }
});

thread.setPriority(Thread.MAX_PRIORITY); // 设置线程优先级为最高
thread.start();

注意:优先级高的线程分配CPU时间片的数量要多于优先级低的线程,并不是优先级高的线程先比优先级低的执行。

setDaemon 设置守护线程

setDaemon方法是Thread类的一个方法,用于将线程设置为守护线程(daemon thread)。

什么是守护线程

守护线程是一种特殊的线程,它不用于程序的正常运行。当程序中所有的非守护线程(用户线程)都结束时,程序会自动退出,即使还有守护线程在运行。守护线程通常用于为用户线程提供服务或者执行后台任务,如垃圾回收器线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hrhcode

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

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

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

打赏作者

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

抵扣说明:

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

余额充值