栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java多线程之Thread

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java多线程之Thread

目录

线程与进程:

创建线程的方式:

线程状态:

 Thread类的静态方法:

Thread类的实例方法:

守护线程isDaeMon()、setDaemon(boolean on)方法:

线程join()方法:

interrupt()、interrupted()、isInterrupted()方法:

wait()和notify()/notifyAll():


线程与进程:

进程可以理解为受操作系统管理的基本运行单元。

线程可以理解为进程中独立运行的子任务。

创建线程的方式:

继承Thread,重写父类的run()方法

定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

创建Thread子类的实例,即创建了线程对象。

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

 实现Runnable接口

定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。 

创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。 

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

通过Callable和FutureTask创建线程

创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

使用FutureTask对象作为Thread对象的target创建并启动新线程。

调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

注意:Callable的call()方法会造成阻塞。

        Thread类也是实现的Runnable接口,Thread和Runnable两种实现方式的对比就在于extends和implements对比,当然是后者好一点。因为第一,继承只能单继承,实现可以多实现;第二,实现的方式对比于单继承的方式,也有利于减小程序间的耦合。

线程状态:

状态说明:

新建状态

使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

就绪状态

当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

运行状态

如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态

如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。

等待阻塞

运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态(wait会释放持有的锁)。

同步阻塞

线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用),则JVM会把该线程放入锁池中。

其他阻塞

通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。(注意,sleep不会释放线程所持有的锁)。

死亡状态

一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

 Thread类的静态方法:

this.XXX()

这种调用方式表示的线程是线程实例本身。

currentThread.XXX()或Thread.XXX()

两种写法是一样的意思。这种调用方式表示的线程是正在执行Thread.currentThread.XXX()所在代码块的线程。

sleep(long millis)

指定的毫秒内让当前"正在执行的线程"休眠(暂停执行),sleep上下文如果被加锁了,锁依然在,但是会让出CPU资源。

yield()

暂停当前执行的线程对象,并执行其他线程。这个暂停是会放弃CPU资源的,并且放弃CPU的时间不确定。

public class MainTest {
    public static void main(String[] args) {
        System.out.println("主线程Thread.currentThread().getId():" + Thread.currentThread().getId());//主线程ID
        System.out.println("主线程Thread.currentThread().getName():" + Thread.currentThread().getName());//主线程名称
        MyThread thread = new MyThread();
        thread.start();//启动线程
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("this.getId():" + this.getId() +  "Thread.currentThread().getId():" +Thread.currentThread().getId());//输出线程ID
        System.out.println("this.getName():" + this.getName() + "Thread.currentThread().getName()" + Thread.currentThread().getName());//输出线程名称
    }
}

         执行结果:

主线程Thread.currentThread().getId():1
主线程Thread.currentThread().getName():main
this.getId():14Thread.currentThread().getId():14
this.getName():Thread-0Thread.currentThread().getName()Thread-0注意: 

        注意:当前执行的Thread未必就是Thread本身。

public class MainTest0 {
    public static void main(String[] args) throws InterruptedException {
        MyThread0 thread = new MyThread0();
        thread.start();
    }
}
class MyThread0 extends Thread {
    static {
        System.out.println("静态代码块打印执行当前代码块的线程名: " + Thread.currentThread().getName());//main
    }
    public MyThread0() {
        System.out.println("构造方法打印执行当前代码块的线程名: " + Thread.currentThread().getName());//main
    }
    @Override
    public void run() {
        System.out.println("线程方法打印执行当前代码块的线程名: " + Thread.currentThread().getName());
    }
}

         执行结果:

静态代码块打印执行当前代码块的线程名: main
构造方法打印执行当前代码块的线程名: main
线程方法打印执行当前代码块的线程名: Thread-0

Thread类的实例方法:

start()

通知"线程规划器",此线程可以运行了,正在等待CPU调用线程对象得run()方法,产生一个异步执行的效果。调用start()方法的顺序不代表线程启动的顺序,线程启动顺序具有不确定性。

run()

线程开始执行,虚拟机调用的是线程run()方法中的内容。(直接调用不具有异步效果)

isAlive()

测试线程是否处于活动状态,只要线程启动且没有终止,方法返回的就是true。

getId()

返回线程的ID

getName()

返回线程的名字

getPriority()

获取线程的优先级(默认为5),线程优先级具有继承性,比如线程A启动线程B,那么线程B的优先级和线程A的优先级相同。

setPriority(int newPriority)

设置线程的优先级,CPU会尽量将执行资源让给优先级比较高的线程。

public class MainTest1 {
    public static void main(String[] args) throws InterruptedException {
        MyThread1 thread = new MyThread1();
        System.out.println("当前线程实例ID: " + thread.getId());
        System.out.println("当前线程实例名称: " + thread.getName());
        System.out.println("当前线程实例优先级: " + thread.getPriority());
        thread.setPriority(3);//设置线程优先级
        System.out.println("设置后当前线程实例优先级: " + thread.getPriority());
        System.out.println("线程是否处于活动状态: " + thread.isAlive());//false
        thread.start();//启动线程
        System.out.println("线程是否处于活动状态: " + thread.isAlive());//true
    }
}
@Slf4j
class MyThread1 extends Thread {
    @Override
    public void run() {
        System.out.println("==线程方法被执行");
        System.out.println("==线程是否处于活动状态:" + this.isAlive());//true
    }
}

        执行结果:

当前线程实例ID: 12
当前线程实例名称: Thread-0
当前线程实例优先级: 5
设置后当前线程实例优先级: 3
线程是否处于活动状态: false
线程是否处于活动状态: true
==线程方法被执行
==线程是否处于活动状态:true

守护线程isDaeMon()、setDaemon(boolean on)方法:

        Java中有两种线程,一种是用户线程,一种是守护线程。守护线程是一种特殊的线程,它的作用是为其他线程的运行提供便利的服务,最典型的便是GC线程。如果进程中不存在非守护线程了,那么守护线程自动销毁。

isDaeMon()

是否为守护线程。

setDaemon(boolean on)

设置守护线程。

public class MainTest2 {
    public static void main(String[] args) throws InterruptedException {
        MyThread2 thread = new MyThread2();
        System.out.println("当前线程实例是否为守护线程: " + thread.isDaemon());//false
        thread.setDaemon(true);//设置为守护线程,setDaemon(true)必须在线程start()之前。
        thread.start();
        Thread.sleep(5000);
        System.out.println("主线程执行结束,守护线程停止执行");
    }
}
class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i=1; i<10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("守护线程执行第: " + i + " 次");
        }
    }
}

        注意,setDaemon(true)必须在线程start()之前。

        执行结果:

当前线程实例是否为守护线程: false
守护线程执行第: 1 次
守护线程执行第: 2 次
守护线程执行第: 3 次
守护线程执行第: 4 次
主线程执行结束,守护线程停止执行
守护线程执行第: 5 次

线程join()方法:

join()

等待线程销毁。

public class MainTest4 {
    public static void main(String[] args) throws InterruptedException {
        MyThread4 thread = new MyThread4();
        thread.start();
        System.out.println(System.currentTimeMillis() + " ->主线程等待子线程执行结束");
        thread.join();
        System.out.println(System.currentTimeMillis() + " ->主线程结束");
    }
}
class MyThread4 extends Thread {
    @Override
    public void run() {
        try {
            System.out.println(System.currentTimeMillis() + " ->线程方法执行");
            Thread.sleep(5000);
            System.out.println(System.currentTimeMillis() + " ->子线程执行结束" );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

        执行结果:

1634997613082 ->主线程等待子线程执行结束
1634997613082 ->线程方法执行
1634997618083 ->子线程执行结束
1634997618083 ->主线程结束

        join()方法会使调用join()方法的线程(也就是thread线程)所在的线程(也就是main线程)无限阻塞,直到调用join()方法的线程销毁为止。

        注意:sleep(2000)不释放锁,join(2000)会释放锁,因为join内部使用的使wait();

public class MainTest4 {
    public static void main(String[] args) throws InterruptedException {
        MyThread4 thread = new MyThread4();
        thread.start();
        System.out.println(System.currentTimeMillis() + " ->主线程等待子线程执行结束");
        thread.join(1000);
        System.out.println(System.currentTimeMillis() + " ->主线程结束");
    }
}
class MyThread4 extends Thread {
    @Override
    public void run() {
        try {
            System.out.println(System.currentTimeMillis() + " ->线程方法执行");
            Thread.sleep(5000);
            System.out.println(System.currentTimeMillis() + " ->子线程执行结束" );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

        执行结果:

1634997656339 ->主线程等待子线程执行结束
1634997656339 ->线程方法执行
1634997657340 ->主线程结束
1634997661339 ->子线程执行结束

interrupt()、interrupted()、isInterrupted()方法:

interrupt()

中断线程,将中断状态设为true

interrupted()

Thread的static方法,测试当前线程是否已经中断,执行后具有将状态标识清除为false的功能。

isInterrupted()

用来判断当前线程的中断状态。线程的中断状态不受这个方法影响。

public class MainTest5 {
    public static void main(String[] args) throws InterruptedException {
        MyThread5 thread = new MyThread5();
        thread.start();
        thread.interrupt();
        System.out.println(System.currentTimeMillis() + " ->线程被中断了,子线程将停止执行。");
    }
}
class MyThread5 extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println(System.currentTimeMillis() + " ->线程方法正在执行");
            if (Thread.currentThread().isInterrupted()) {
                System.out.println(System.currentTimeMillis() + " ->线程被中断了");
                return;
            }
        }
    }
}

         执行结果:

1635000122974 ->线程被中断了,子线程将停止执行。
1635000122974 ->线程方法正在执行
1635000122974 ->线程被中断了

wait()和notify()/notifyAll():

     线程本身是操作系统中独立的个体,但是线程与线程之间不是独立的个体,因为它们彼此之间要相互通信和协作(线程之间通信)。

wait()

使当前执行代码的线程进行等待,将当前线程置入"预执行队列"中,并且wait()所在的代码处停止执行,直到接到通知或被中断。 在调用wait()之前,线程必须获得该对象的锁,因此只能在同步方法/同步代码块中调用wait()方法。

notify()

如果有多个线程等待,那么线程规划器随机挑选出一个wait的线程,对其发出通知notify(),并使它等待获取该对象的对象锁。

notifyAll()

将唤醒所有等待当前线程所拥有的锁的线程,但只有一个被唤醒的线程获得锁。

        注意:"等待获取该对象的对象锁",这意味着,即使收到了通知,wait的线程也不会马上获取对象锁,必须等待notify()方法的线程释放锁才可以。和wait()一样,notify()也要在同步方法/同步代码块中调用。

     总结起来就是,wait()使线程停止运行,notify()使停止运行的线程继续运行。

public class MainTest6 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        MyThread6 thread = new MyThread6(lock);
        MzThread thread1 = new MzThread(lock);
        thread.start();
        Thread.sleep(100);
        thread1.start();
    }
}
class MyThread6 extends Thread {
    private Object lock;
    public MyThread6(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized (lock) {
                System.out.println("wait 程序开始运行,时间为: " + System.currentTimeMillis() + " 需要等待另一个程序运行结束。");
                lock.wait();
                Thread.sleep(5000);
                System.out.println("wait 程序运行结束,时间为: " + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MzThread extends Thread {
    private Object lock;
    public MzThread(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized (lock) {
                System.out.println("notify 程序开始运行,时间为: " + System.currentTimeMillis());
                lock.notify();
                Thread.sleep(5000);
                System.out.println("notify 程序运行结束,时间为: " + System.currentTimeMillis() + " 唤醒等待的程序继续执行。");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

        执行结果:

wait 程序开始运行,时间为: 1635001695818 需要等待另一个程序运行结束。
notify 程序开始运行,时间为: 1635001695927
notify 程序运行结束,时间为: 1635001700935 唤醒等待的程序继续执行。
wait 程序运行结束,时间为: 1635001705946

        wait()执行后会立即释放锁,而notify()不会立即释放锁,必须执行完notify()方法所在的synchronized代码块后才释放(notify线程执行notify()方法后,sleep了5s)!

wait()方法可以使调用该线程的方法释放共享资源锁,然后从运行状态退出,进入等待队列,直到再次唤醒。

notify()方法可以随机唤醒等待队列中同一共享资源的一个线程,并使得该线程退出等待状态,进入可运行状态。

notifyAll()方法可以使所有正在等待队列中等待同一共享资源的全部线程从等待状态退出,进入可运行状态。

注意:如果wait()方法和notify()/notifyAll()方法不再同步方法/同步代码块中被调用,那么虚拟机会抛出java.lang.IllegalMonitorStateException。

        interrupt()打断wait()。

public class MainThread7 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        MyThread7 thread = new MyThread7(lock);
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}
class MyThread7 extends Thread {
    private Object lock;
    public MyThread7(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        MyDomain domain = new MyDomain();
        domain.test(lock);
    }
}
class MyDomain  {
    public void test(Object lock) {
        try {
            synchronized (lock) {
                System.out.println(System.currentTimeMillis() + " -> 开始wait");
                lock.wait();
                System.out.println(System.currentTimeMillis() + " -> 结束wait");
            }
        } catch (InterruptedException e) {
            System.out.println(System.currentTimeMillis() + " ->wait被interrupt()打断了");
        }
    }
}

        执行结果:

1635003944590 -> 开始wait
1635003945615 ->wait被interrupt()打断了

        notifyAll()唤醒所有线程:利用Object对象的notifyAll()方法可以唤醒处于同一监视器下的所有处于wait的线程。

public class MainTest8 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        MyThread8 thread = new MyThread8(lock);
        MyThread8 thread1 = new MyThread8(lock);
        MyThread8 thread2 = new MyThread8(lock);
        MzThread mzThread = new MzThread(lock);
        thread.start();
        thread1.start();
        thread2.start();
        Thread.sleep(1000);
        mzThread.start();
    }
}
class MyThread8 extends Thread {
    private Object lock;
    public MyThread8(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        MyDomain8 domain = new MyDomain8();
        domain.test(lock);
    }
}
class MzThread8 extends Thread {
    private Object lock;
    public MzThread8(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock) {
            lock.notifyAll();
        }
    }
}
class MyDomain8  {
    public void test(Object lock) {
        try {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + System.currentTimeMillis() + " -> 开始wait ");
                lock.wait();
                System.out.println(Thread.currentThread().getName() + System.currentTimeMillis() + " -> 结束wait ");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

         执行结果:

Thread-2Thread-2开始wait 
Thread-0Thread-0开始wait 
Thread-1Thread-1开始wait 
notify 程序开始运行,时间为: 1635005542623
notify 程序运行结束,时间为: 1635005547629 唤醒等待的程序继续执行。
Thread-2Thread-2结束wait 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/346166.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号