java并发原理实战(2)--线程的状态和切换

简介: java并发原理实战(2)--线程的状态和切换

高效并发


1.join的理解


join源码中,只会调用wait方法,并没有在结束时调用notify,这是因为线程在die的时候会自动调用自身的notifyAll方法,来释放所有的资源和锁。


2.sleep的理解


调用sleep()之后,会引起当前执行的线程进入暂时中断状态,也即睡眠状态。


中断完成之后,自动进入唤醒状态从而继续执行代码。


3.wait的理解


关键点: wait是Object的方法,必须在同步代码中使用,通过notice/noticeAll方法唤醒。

代码演示:

1dc618a0ed9580ce8bfa6facb208c08f.png


4.线程状态


《java核心技术-卷1》


线程可以有6种状态:


New(新建)

Runnable(可运行)

Blocked(被阻塞)

Waiting(等待)

Timed waiting(计时等待)

Terminated(被终止)

New:new Thread()后线程的状态就是新建。


Runnable:线程一旦调用start()方法,无论是否运行,状态都为Runable,注意Runable状态指示表示线程可以运行,不表示线程当下一定在运行,线程是否运行由虚拟机所在操作系统调度决定。


被阻塞:线程试图获取一个内部对象的Monitor(进入synchronized方法或synchronized块)但是其他线程已经抢先获取,那此线程被阻塞,知道其他线程释放Monitor并且线程调度器允许当前线程获取到Monitor,此线程就恢复到可运行状态。


等待:当一个线程等待另一个线程通知调度器一个条件时,线程进入等待状态。


计时等待:和等待类似,某些造成等待的方法会允许传入超时参数,这类方法会造成计时等待,收到其他线程的通知或者超时都会恢复到可运行状态。


被终止:线程执行完毕正常结束或执行过程中因未捕获异常意外终止都会是线程进入被终止状态。


5.线程状态转换



1dc618a0ed9580ce8bfa6facb208c08f.png

观察状态转化图,我们发现“可运行”状态为所有状态的必经状态。我们分析出四条基本的状态转换线路图。


新建—>可运行—>被终止

新建—>可运行—>被阻塞—>可运行—>被终止

新建—>可运行—>等待—>可运行—>被终止

新建—>可运行—>计时等待—>可运行—>被终止

“新建”和“被终止”状态分别为起始和结束状态,和“可运行”状态不可逆。其他状态均能和“可运行”状态相互转换。


用代码说话


让我们用代码演示线程状态是如何转换的,大家重点关注两个问题?


什么操作会改变线程状态?


改变的状态是如何恢复的?


① 新建—>可运行—>被终止


这个状态转换时创建的线程生命周期。


/**
 * NEW->RUNNABLE->TERMINATED
 */
public class ThreadStateNRT {
    public static void main(String[] args) {
        Thread thread=new Thread(new Task());
        print(thread.getName(),thread.getState());
        thread.start();
        //等待线程执行完毕。
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        print(thread.getName(),thread.getState());
    }
    private static class Task implements Runnable{
        @Override
        public void run() {
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
    private static final String stringFormat="%s:%s";
    private static void print(String threadName,Thread.State state){
        System.out.println(String.format(stringFormat,threadName,state));
    }
}


其中,print()方法用来打印线程信息。后面的代码示例均不在展示。


运行程序结果为:


Thread-0:NEW

Thread-0:RUNNABLE

Thread-0:TERMINATED


② 新建—>可运行—>被阻塞—>可运行—>被终止


只有一种方法能出现阻塞状态,那就是synchronized同步原语。我们需要两个线程其中一个线程被另一个阻塞来展示。

/**
 * NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED
 */
public class ThreadStateNRBRT {
    //锁
    private static final Object lock=new Object();
    public static void main(String[] args) {
        //辅助线程,制造synchronized状态。
        Thread assistantThread = new Thread(new SynTask());
        assistantThread.start();
        try {
            //保证assistantThread先执行。
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread showThread = new Thread(new Task());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //因为无法判断显示线程何时执行,所以循环直到显示线程执行。
        while (true){
            if(showThread.getState()==Thread.State.BLOCKED){
                print(showThread.getName(), Thread.State.BLOCKED);
                break;
            }
        }
        //等待两个线程执行完毕。
        try {
            assistantThread.join();
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }
    private static class SynTask implements Runnable {
        @Override
        public void run() {
            //锁定一定时间
            synchronized (lock){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    private static class Task implements Runnable {
        @Override
        public void run() {
            synchronized (lock){
                print(Thread.currentThread().getName(),Thread.currentThread().getState());
            }
        }
    }
}


执行一下你有可能看到正确结果:


Thread-1:NEW
Thread-1:RUNNABLE
Thread-1:BLOCKED
Thread-1:RUNNABLE
Thread-1:TERMINATED


为什么是有可能呢?我们调整一下代码,例如将加锁的时间调小一点:


private static class SynTask implements Runnable {
        @Override
        public void run() {
            //锁定一定时间
            synchronized (lock){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
}


注意此处Thread.sleep(10),我们只锁住十毫秒。


再运行一下,控制台可能打印出这样的结果且程序不会结束:


Thread-1:NEW
Thread-1:RUNNABLE
Thread-1:RUNNABLE

造成以上结果的原因是我么无法保证两个线程的执行顺序,也无法证主线程一定能打印出显示线程阻塞的状态。


   

while (true){
            if(showThread.getState()==Thread.State.BLOCKED){
                print(showThread.getName(), Thread.State.BLOCKED);
                break;
            }
        }


所以执行在这段代码死循环了。


调整一下代码,保证不会因为参数调整改变线程之间的执行顺序和打印结果。


/**
 * NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED
 */
public class ThreadStateNRBRT_New {
    //锁
    private static final Object lock=new Object();
    //锁定标志
    private volatile static boolean lockFlag=true;
    //执行顺序
    private volatile static int order=0;
    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new Task());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(), showThread.getState());
        //辅助线程,制造synchronized状态。
        Thread assistantThread = new Thread(new SynTask());
        assistantThread.start();
        //循环读取展示线程状态,直到读到展示线程状态为BLOCKED,才让辅助线程退出同步。
        while (true){
            if(showThread.getState()==Thread.State.BLOCKED){
                print(showThread.getName(), Thread.State.BLOCKED);
                lockFlag=false;
                break;
            }
        }
        try {
            assistantThread.join();
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }
    private static class SynTask implements Runnable {
        @Override
        public void run() {
            while (true) {
                //保证先进入同步范围。
                if (order == 0) {
                    synchronized (lock) {
                        //启动另一个同步
                        order=1;
                        //等待主线程读取到线程阻塞状态,退出同步。
                        while (lockFlag) {
                            try {
                                Thread.sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    break;
                }
            }
        }
    }
    private static class Task implements Runnable {
        @Override
        public void run() {
            while (true){
                //保证后进入同步范围。
                if (order==1){
                    synchronized (lock){
                        print(Thread.currentThread().getName(),Thread.currentThread().getState());
                    }
                    break;
                }
            }
        }
    }
}


我们用order保证线程进入同步区的顺序,用lockFlag保证只有在打印出显示线程的被阻塞状态后辅助线程才退出同步区。这样无论如何执行我们都会得到同样的结果。


Thread-0:NEW
Thread-0:RUNNABLE
Thread-0:BLOCKED
Thread-0:RUNNABLE
Thread-0:TERMINATED

③ 新建—>可运行—>等待—>可运行—>被终止


这里我们展示两种三种方法造成线程的等待状态


Object.wait()
java.util.concurrent.locks.Locke.lock()
java.util.concurrent.locks.Condition.await()

其他方法如Thread.join()等大家可以参考示例代码自己实现。


1. Object.wait()


/**
 * NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRWRT {
    //锁
    private static final Object lock=new Object();
    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new WaitTask());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //循环读取展示线程状态,直到读到展示线程状态为WAITING,才让辅助线程唤醒等待线程。
        while (true){
            if(showThread.getState()==Thread.State.WAITING){
                print(showThread.getName(), Thread.State.WAITING);
                break;
            }
        }
        synchronized (lock){
            lock.notify();
        }
        try {
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }
    private static class WaitTask implements Runnable {
        @Override
        public void run() {
            //等待
            synchronized (lock){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
}


2. java.util.concurrent.locks.Locke.lock()


/**
 * NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRWRTLock {
    //锁
    private  static Lock lock=new ReentrantLock();
    //锁定标志
    private volatile static boolean lockFlag=true;
    //执行顺序
    private volatile static int order=0;
    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new Task());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(), showThread.getState());
        //辅助线程,制造synchronized状态。
        Thread assistantThread = new Thread(new SynTask());
        assistantThread.start();
        //循环读取展示线程状态,直到读到展示线程状态为BLOCKED,才让辅助线程退出同步。
        while (true){
            if(showThread.getState()==Thread.State.WAITING){
                print(showThread.getName(), Thread.State.WAITING);
                lockFlag=false;
                break;
            }
        }
        try {
            assistantThread.join();
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }
    private static class SynTask implements Runnable {
        @Override
        public void run() {
            while (true) {
                //保证先进入同步范围。
                if (order == 0) {
                    //加锁
                    lock.lock();
                    try {
                        order=1;
                        while (lockFlag) {
                            try {
                                Thread.sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }finally {
                        lock.unlock();
                    }
                    break;
                }
            }
        }
    }
    private static class Task implements Runnable {
        @Override
        public void run() {
            while (true){
                //保证后进入同步范围。
                if (order==1){
                    lock.lock();
                    try{
                        print(Thread.currentThread().getName(),Thread.currentThread().getState());
                    }finally {
                        lock.unlock();
                    }
                    break;
                }
            }
        }
    }
}


3. java.util.concurrent.locks.Condition.await()


/**
 * NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRWRTCondition {
    //锁
    private static Lock lock=new ReentrantLock();
    private static Condition condition=lock.newCondition();
    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new WaitTask());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //循环读取展示线程状态,直到读到展示线程状态为WAITING,才让辅助线程唤醒等待线程。
        while (true){
            if(showThread.getState()==Thread.State.WAITING){
                print(showThread.getName(), Thread.State.WAITING);
                break;
            }
        }
        lock.lock();
        try{
            condition.signal();
        }finally {
            lock.unlock();
        }
        try {
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }
    private static class WaitTask implements Runnable {
        @Override
        public void run() {
            //等待
            lock.lock();
            try{
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
                lock.unlock();
            }
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
}


4. 运行结果


三段代码的运行结果都是:


Thread-0:NEW
Thread-0:RUNNABLE
Thread-0:WAITING
Thread-0:RUNNABLE
Thread-0:TERMINATED


④新建—>可运行—>计时等待—>可运行—>被终止


我们展示两个方法造成计时等待状态


Object.wait(long timeout)

java.util.concurrent.locks.Condition.await(long time, TimeUnit unit)

其他方法如Thread.sleep(long millis),Thread.join(long millis)等大家可以自己实现。

感觉凡是有超时方法的方法都能让线程状态进入计时等待,但是这个没有经过验证,所以只是一个猜想。


1. Object.wait(long timeout)


/**
 * NEW->RUNNABLE->TIMED_WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRTWRT {
    //锁
    private static final Object lock=new Object();
    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new WaitTask());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //循环读取展示线程状态,直到读到展示线程状态为TIMED_WAITING。
        while (true){
            if(showThread.getState()==Thread.State.TIMED_WAITING){
                print(showThread.getName(), Thread.State.TIMED_WAITING);
                break;
            }
        }
        try {
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }
    private static class WaitTask implements Runnable {
        @Override
        public void run() {
            //等待
            synchronized (lock){
                try {
                    lock.wait(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
}


2. java.util.concurrent.locks.Condition.await(long time, TimeUnit unit)


/**
 * NEW->RUNNABLE->TIMED_WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRTWRTCondition {
    //锁
    private static Lock lock=new ReentrantLock();
    private static Condition condition=lock.newCondition();
    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new WaitTask());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //循环读取展示线程状态,直到读到展示线程状态为TIMED_WAITING。
        while (true){
            if(Thread.State.TIMED_WAITING==showThread.getState()){
                print(showThread.getName(), Thread.State.TIMED_WAITING);
                break;
            }
        }
        try {
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }
    private static class WaitTask implements Runnable {
        @Override
        public void run() {
            //等待
            lock.lock();
           try{
                try {
                    condition.await(1,TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
               lock.unlock();
           }
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
}


3. 运行结果


两段程序的运行结果相同:


Thread-0:NEW
Thread-0:RUNNABLE
Thread-0:TIMED_WAITING
Thread-0:RUNNABLE
Thread-0:TERMINATED



相关文章
|
20天前
|
并行计算 算法 安全
面试必问的多线程优化技巧与实战
多线程编程是现代软件开发中不可或缺的一部分,特别是在处理高并发场景和优化程序性能时。作为Java开发者,掌握多线程优化技巧不仅能够提升程序的执行效率,还能在面试中脱颖而出。本文将从多线程基础、线程与进程的区别、多线程的优势出发,深入探讨如何避免死锁与竞态条件、线程间的通信机制、线程池的使用优势、线程优化算法与数据结构的选择,以及硬件加速技术。通过多个Java示例,我们将揭示这些技术的底层原理与实现方法。
71 3
|
21天前
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
30 3
|
21天前
|
安全 算法 Java
Java CAS原理和应用场景大揭秘:你掌握了吗?
CAS(Compare and Swap)是一种乐观锁机制,通过硬件指令实现原子操作,确保多线程环境下对共享变量的安全访问。它避免了传统互斥锁的性能开销和线程阻塞问题。CAS操作包含三个步骤:获取期望值、比较当前值与期望值是否相等、若相等则更新为新值。CAS广泛应用于高并发场景,如数据库事务、分布式锁、无锁数据结构等,但需注意ABA问题。Java中常用`java.util.concurrent.atomic`包下的类支持CAS操作。
56 2
|
2月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
2月前
|
安全 Java
线程安全的艺术:确保并发程序的正确性
在多线程环境中,确保线程安全是编程中的一个核心挑战。线程安全问题可能导致数据不一致、程序崩溃甚至安全漏洞。本文将分享如何确保线程安全,探讨不同的技术策略和最佳实践。
54 6
|
2月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
77 6
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
8天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
48 17
|
19天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
4天前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题