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



相关文章
|
5天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
25 9
|
5天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
6天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
7天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
18 1
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
43 1
C++ 多线程之初识多线程
|
23天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
16 3
|
23天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
16 2
|
23天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
28 2
|
23天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
28 1
|
23天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
33 1