多线程基础体系知识清单(中)

简介: 多线程基础体系知识清单(中)

实现方式1(错误示例):


public class Main implements Callable<String>{
    @Override
    public String call() throws Exception {
        // TODO Auto-generated method stub
        String str = "我是带返回值的子线程";
        return str;
    }
    public static void main(String[] args) {
        Main main = new Main();
        try {
            String str = main.call();
            System.out.println(str);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}


运行结果:


image.png


实现方式2(使用FutureTask):


public class Main implements Callable<String>{
    @Override
    public String call() throws Exception {
        // TODO Auto-generated method stub
        String str = "我是带返回值的子线程";
        return str;
    }
    public static void main(String[] args) {
        FutureTask<String> task = new FutureTask<String>(new Main());
        new Thread(task).start();
        try {
            if(!task.isDone()) {
                System.out.println("任务没有执行完成");
            }
            System.out.println("等待中...");
            Thread.sleep(3000);
            System.out.println(task.get());
        } catch (InterruptedException | ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}


运行结果:


image.png


实现方法3(使用线程池配合Future获取):


public class Main implements Callable<String>{
    @Override
    public String call() throws Exception {
        // TODO Auto-generated method stub
        String str = "我是带返回值的子线程";
        return str;
    }
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService newCacheThreadPool = Executors.newCachedThreadPool(); 
        Future<String> future = newCacheThreadPool.submit(new Main());
        if(!future.isDone()) {
            System.out.println("线程尚未执行结束");
        }
        System.out.println("等待中");
        Thread.sleep(300);
        System.out.println(future.get());
        newCacheThreadPool.shutdown();
    }
}


运行结果:


image.png



线程的状态


Java线程主要分为以下六个状态:新建态(new),运行态(Runnable),无限期等待

(Waiting),限期等待(TimeWaiting),阻塞态(Blocked),结束(Terminated)


新建(new)


新建态是线程处于已被创建但没有被启动的状态,在该状态下的线程只是被创建出来了,但并没有开始执行其内部逻辑。


运行(Runnable)


运行态分为Ready和Running,当线程调用start方法后,并不会立即执行,而是去争夺CPU,当线程没有开始执行时,其状态就是Ready,而当线程获取CPU时间片后,从Ready态转为Running态。



等待(Waiting)


处于等待状态的线程不会自动苏醒,而只有等待被其它线程唤醒,在等待状态中该线程不会被CPU分配时间,将一直被阻塞。以下操作会造成线程的等待:


  1. 没有设置timeout参数的Object.wait()方法。
  2. 没有设置timeout参数的Thread.join()方法。
  3. LockSupport.park()方法(实际上park方法并不是LockSupport提供的,而是在Unsafe中,LockSupport只是对其做了一层封装,可以看我的另一篇博客《锁》,里面对于ReentrantLock的源码解析有提到这个方法)。


锁:https://juejin.im/post/5d8da403f265da5b5d203bf4


限期等待(TimeWaiting)


处于限期等待的线程,CPU同样不会分配时间片,但存在于限期等待的线程无需被其它线程显式唤醒,而是在等待时间结束后,系统自动唤醒。以下操作会造成线程限时等待:

  1. Thread.sleep()方法。

  2. 设置了timeout参数的Object.wait()方法。
  3. 设置了timeout参数的Thread.join()方法。
  4. LockSupport.parkNanos()方法。
  5. LockSupport.parkUntil()方法。


阻塞(Blocked)


当多个线程进入同一块共享区域时,例如Synchronized块、ReentrantLock控制的区域等,会去整夺锁,成功获取锁的线程继续往下执行,而没有获取锁的线程将进入阻塞状态,等待获取锁。


结束(Terminated)


已终止线程的线程状态,线程已结束执行。


Sleep和Wait的区别


Sleep和Wait者两个方法都可以使线程进入限期等待的状态,那么这两个方法有什么区别呢?


  1. sleep方法由Thread提供,而wait方法由Object提供。
  2. sleep方法可以在任何地方使用,而wait方法只能在synchronized块或synchronized方法中使用(因为必须获wait方法会释放锁,只有获取锁了才能释放锁)。
  3. sleep方法只会让出CPU,不会释放锁,而wait方法不仅会让出CPU,还会释放锁。


测试代码:


public class Main{
    public static void main(String[] args) {
        Thread threadA = new Thread(new ThreadA());
        Thread threadB = new Thread(new ThreadB());
        threadA.setName("threadA");
        threadB.setName("threadB");
        threadA.start();
        threadB.start();
    }
    public static synchronized void print() {
        System.out.println("当前线程:"+Thread.currentThread().getName()+"执行Sleep");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("当前线程:"+Thread.currentThread().getName()+"执行Wait");
        try {
            Main.class.wait(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("当前线程:"+Thread.currentThread().getName()+"执行完毕");
    }
}
class ThreadA implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        Main.print();
    }
}
class ThreadB implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        Main.print();
    }
}



执行结果:


image.png


从上面的结果可以分析出:当线程A执行sleep后,等待一秒被唤醒后继续持有锁,执行之后的代码,而执行wait之后,立即释放了锁,不仅让出了CPU还让出了锁,而后线程B立即持有锁开始执行,和线程A执行了同样的步骤,当线程B执行wait方法之后,释放锁,然后线程A拿到锁打印了第一个执行完毕,然后线程B打印执行完毕。



相关文章
|
6月前
|
敏捷开发 监控 安全
PMBOK泛读(第四章) - 项目整合管理(一)
PMBOK泛读(第四章) - 项目整合管理
41 0
|
8月前
|
项目管理
深入解析PMP项目内部运营环境
在项目管理领域,PMP认证是一项备受尊敬的资格,它强调了对项目内部运营环境的深刻理解。PMP专业人士不仅需要了解项目管理的基本原则,还需要熟悉项目内部运营环境的方方面面。本文将深入探讨PMP项目内部运营环境的重要性以及如何有效管理这一环境。
|
3天前
|
小程序 UED
人力资源小程序的设计与开发步骤
人力资源小程序的设计与开发步骤
25 1
|
3天前
|
JavaScript Java 测试技术
基于Java的大学生成果登记系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的大学生成果登记系统的设计与实现(源码+lw+部署文档+讲解等)
20 0
|
3天前
|
JavaScript Java 测试技术
基于Java的贫困认定管理平台的设计与实现(源码+lw+部署文档+讲解等)
基于Java的贫困认定管理平台的设计与实现(源码+lw+部署文档+讲解等)
26 1
|
6月前
|
数据可视化 数据挖掘 测试技术
PMBOK泛读(第五章) - 项目范围管理(一)
PMBOK泛读(第五章) - 项目范围管理
55 0
|
6月前
|
自然语言处理 数据挖掘 项目管理
PMBOK泛读(第五章) - 项目范围管理(二)
PMBOK泛读(第五章) - 项目范围管理(二)
38 0
|
6月前
|
数据挖掘 项目管理 索引
PMBOK泛读(第四章) - 项目整合管理(四)
PMBOK泛读(第四章) - 项目整合管理(四)
31 0
|
6月前
|
监控 数据挖掘 项目管理
PMBOK泛读(第四章) - 项目整合管理(三)
PMBOK泛读(第四章) - 项目整合管理(三)
29 0
|
7月前
|
监控 安全 数据挖掘
泰山众筹系统开发详细指南丨设计方案丨规则玩法丨逻辑功能丨步骤需求丨源码程序
泰山众筹系统是一个基于区块链技术的众筹平台,旨在为用户提供一个安全、透明和高效的众筹环境。