wait放弃对象锁_多线程,从入门到放弃

本文介绍了线程的基本概念,重点解析了Java中线程的六种状态及其转换,包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED。此外,还详细阐述了sleep()、wait()、join()、yield()和interrupt()这五个关键方法的作用,以及它们如何影响线程的执行和同步。
摘要由CSDN通过智能技术生成
bafcb9f599da62783587604a7dd7746a.png

线程是什么?

线程的本质一个轻量级的过程,它是代码序列以及所有数据支持结构(例如,打开的资源,内存映射,堆栈等)的执行。如果要并行运行代码,将使编程变得容易。它受益于多CPU的体系结构,它还可以运行多个进程或在一个进程中运行多个线程。

Java中的线程

线程状态

我们看到 Java 源代码里面,线程状态的枚举有如下 6 个:

 public enum State {        /**         * Thread state for a thread which has not yet started.         */        NEW,        /**         * Thread state for a runnable thread.          */        RUNABLE,        /**         * Thread state for a thread blocked waiting for a monitor lock.         * A thread in the blocked state is waiting for a monitor lock         */        BLOCKED,        /**         * Thread state for a waiting thread.         * A thread is in the waiting state due to calling one of the         * following methods:         * 
* {@link Object#wait() Object.wait} with no timeout * {@link #join() Thread.join} with no timeout * {@link LockSupport#park() LockSupport.park} * */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * * {@link #sleep Thread.sleep} * {@link Object#wait(long) Object.wait} with timeout * {@link #join(long) Thread.join} with timeout * {@link LockSupport#parkNanos LockSupport.parkNanos} * {@link LockSupport#parkUntil LockSupport.parkUntil} * */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }

下面给这 6 个状态一一做下解释。

NEW:新建状态。在创建完 Thread ,还没执行 start() 之前,线程的状态一直是 NEW。

RUNNABLE:运行状态。线程对象调用 start() 之后,就进入 RUNNABLE 状态,对应操作环境中的RUNNABLE和RUNNING两种状态。

BLOCKED:阻塞状态。线程在等待获取对象锁,线程处于“Entry Set”区域。

WAITING:等待状态。线程在这个状态的时候,不会被分配 CPU,而且需要被显示地唤醒,否则会一直等待下去。

TIMED_WAITING:超时等待状态。这个状态的线程也一样不会被分配 CPU,但是它不会无限等待下去,有时间限制,时间一到就停止等待。

TERMINATED:终止状态。线程执行完成结束,但不代表这个对象已经没有了,对象可能还是存在的,只是线程不存在了。

线程既然有这么多个状态,那肯定就有状态机,也就是在什么情况下 A 状态会变成 B 状态。下面就来简单描述一下。

结合下图,我们 new 出线程类的时候,就是 NEW 状态,调用 start() 方法,就进入了 RUNNABLE 状态,这时如果触发等待,则进入了 WAITING 状态,如果触发超时等待,则进入 TIMED_WAITING 状态,当访问需要同步的资源时,则只有一个线程能访问,其他线程就进入 BLOCKED 状态,当线程执行完后,进入 TERMINATED 状态。

8d470c269b71163da289deced2439887.png

java状态

1、Java 中实现多线程有几种方法

(1)继承 Thread 类;

(2)实现 Runnable 接口;

(3)实现 Callable 接口通过 FutureTask 包装器来创建 Thread 线程;

(4)使用 ExecutorService、Callable、Future 实现有返回结果的多线程(也就是使用了 ExecutorService 来管理前面的三种方式)。

2、sleep()、wait()、join()、yield()、interrupt()方法功效

1) sleep()

线程中的方法,不会释放lock,休眠时间到之后自动退出Blocked。代码如下:

public class TestSleep {    private static void test() {            try {                System.out.println(Thread.currentThread().getName() + "正在执行");                Thread.sleep(1000);                System.out.println(Thread.currentThread().getName() + "休眠结束");            } catch (InterruptedException e) {                e.printStackTrace();            }    }    public static void main(String[] args) {        Stream.of("线程1", "线程2").forEach(s -> new Thread(s) {            public void run() {                TestSleep.test();            }        }.start());    }}

运行结果如下:

9da1962eaa4c747f0b564bf37759585a.png

2) wait()

Object中的方法,会释放lock,需要notify之后才会退出Blocked。我们用存钱、取钱的代码举例:

public class TestWait {    private static Object lock = new Object();    static int amount = 100; // 账户初始100元    public static void deposit(int cash) { // 存钱        synchronized (lock) {            amount += cash;            lock.notify();            try {                Thread.sleep(30000); // 通知后却暂时不退出            } catch (InterruptedException e) {                throw new RuntimeException(e);            }        }    }    public static void withdraw(int cash) { // 取钱        synchronized (lock) {            while (cash > amount) {                try {                    lock.wait();                } catch (InterruptedException e) {                    throw new RuntimeException(e);                }            }            amount -= cash;        }    }    public static void main(String[] args) throws Exception {        Thread threadWithdraw = new Thread("取钱线程") {            @Override            public void run() {                com.kanq.TestSleep.withdraw(200);            }        };        threadWithdraw.start();        Thread.sleep(1000); //确保取钱线程已经得到执行        System.out.println(threadWithdraw.getState());        Thread threadDeposit = new Thread("存钱线程") {            @Override            public void run() {                com.kanq.TestSleep.deposit(100);            }        };        threadDeposit.start();        Thread.sleep(1000);//确保存钱线程已经得到执行        System.out.println(threadWithdraw.getState());    }}

运行结果如下:

7b94507eeca80c568383070639e0ba2c.png

取钱线程收到通知后,退出 WAITING 状态,但已经不持有锁,当试图重新进入(reenter)同步块以恢复执行时,因锁尚未被存钱线程释放,于是被阻塞(BLOCKED 状态)

3) join()

主线程会等待子线程执行结束,再继续执行。底层用wait实现。不会影响已经在执行的其他线程。

public class TestJoin {    public static void deposit(int cash) { // 存钱        try {            Thread.sleep(3000); // 通知后却暂时不退出            System.out.println(Thread.currentThread().getName() + "休眠結束");        } catch (InterruptedException e) {            throw new RuntimeException(e);        }    }    public static void main(String[] args) throws Exception {        Thread threadDeposit = new Thread("存钱线程") {            @Override            public void run() {                TestJoin.deposit(100);            }        };        threadDeposit.start();        threadDeposit.join();        System.out.println(Thread.currentThread().getName() + "执行了");    }}

运行结果如下:

9340f31e707de6db7280de53b11c4c03.png

4) yield()

放弃当前线程获取CPU的执行权,使当前线程重新回到可执行状态。执行yield()的线程有可能在进入到可执行状态后马上又被执行。

public class TestYield {    public static void deposit(){ // 存钱        for (int i = 1; i <= 10; i++) {            if (i==3) {                Thread.yield();            }            System.out.println("" + Thread.currentThread().getName() + "-----" + i);        }    }    public static void main(String[] args){        new Thread("线程1") {            @Override            public void run() {                TestYield.deposit();            }        }.start();        new Thread("线程2") {            @Override            public void run() {                TestYield.deposit();            }        }.start();    }}

执行结果如下:

432483ce3a3474129dd40807de62a088.png

5) interrupt()

改变线程的中断状态,至于这个中断状态改变后带来的结果,无法确定。有时它更是让停止中的线程继续执行的唯一手段。不但不是让线程停止运行,反而是继续执行线程的手段。它存在的意义:我们可以根据中断标志的具体值,来决定线程下一步执行什么。

在一个线程对象上调用interrupt()方法,真正有影响的是wait,join,sleep方法。其他状态的线程,执行过后,并没有什么影响。

public class TestInterrupte {    public static void deposit() { // 存钱        try {            Thread.sleep(3000); // 通知后却暂时不退出            System.out.println(Thread.currentThread().getName() + "休眠結束");        } catch (InterruptedException e) {            System.out.println(Thread.currentThread().getName() + "被中断了");            throw new RuntimeException(e);        }    }    public static void main(String[] args) throws Exception {        Thread threadDeposit = new Thread("存钱线程") {            @Override            public void run() {                TestInterrupte.deposit();            }        };        threadDeposit.start();        threadDeposit.interrupt();    }}

执行结果如下:

979bc0eeee22b4f95f09b9068b8892fd.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值