JUC系列-基础知识

  • 线程启动
    • 代码示例
      • 继承Thread类
      • 实现Runnable接口
      • 利用FutureTask
  • 线程常用方法
    • 线程通知与等待
      • wait方法
      • notify方法
      • 生产者与消费者代码示例
    • 常用Thread类方法
      • join方法
      • interrupt方法
      • sleep方法
      • yield方法
    • 守护线程与用户线程

线程启动

通常启动有三种方法启动线程:
1、继承Thread类,重写run()方法,启动线程。
2、实现Runnable接口,重写run()方法,将实现作为线程构造传入,启动线程。
3、用FutureTask包装Runnable或者Callable,可以返回线程执行结果。

代码示例

继承Thread类

class MyThread extends Thread {public MyThread(String name) {super(name);}@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}}public static void main(String[] args){Thread t1 = new MyThread("test");t1.start();
}

实现Runnable接口

public static void main(String[] args){Runnable r = () -> System.out.println(Thread.currentThread().getName());Thread aThread = new Thread(r, "a");aThread.start();
}

利用FutureTask

1、Future接口用来表示异步执行的结果,其主要方法定义如下:
V get() 获取执行结果,如果执行未结束则会阻塞。
V get(long timeout, TimeUnit unit) 获取执行结果,如果执行未结束则会阻塞指定的时间。
boolean cancel(boolean mayInterruptIfRunning) 取消当前线程任务。参数为true代表取消正在执行任务的线程,false代表不取消正在执行任务的线程;返回值为取消结果,典型场景是任务已经完成调用此方法会返回false。
boolean isDone() 返回true如果线程已经完成,完成的场景包括正常结束、异常或者被取消等。
boolean isCancelled() 返回true如果线程在正常完成之前被取消。

2、Callable接口,定义类似于Runnable,只有一个call()方法,并且有返回值。在JUC中如果需要获取异步执行结果,通常使用该接口实现。

3、RunnableFuture是同时实现RunnableFuture的接口。而FutureTaskRunnableFuture的实现类,其主要方法是:run()执行异步任务,get()获取任务结果。

4、FutureTask 获取任务结果的定义接口来自Future,具体的功能实现来自于Callable接口,其内部持有一个Callable对象,在run()执行异步任务时,实际上执行的是Callable对象的call()方法,并把结果保存在outcome字段中。调用get()方法时,会判断线程状态,如果还未完成则会阻塞挂起,WaitNode变量代表等待的线程队列。在run()方法中任务完成之后,finishCompletion()会唤醒等待获取结果的线程。

public static void main(String[] args){Callable<Integer> callable = () -> {int sum = 0;for (int i = 1; i < 20; i++) {sum += i;}System.out.println(Thread.currentThread().getName());return sum;};FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread callThread = new Thread(futureTask, "call");callThread.start();System.out.println("sum: " + futureTask.get());//future.get()会发生阻塞,等待异步计算完成获取结果
}

线程常用方法

线程通知与等待

主要包括wait()、notify()、notifyAll()方法,属于Object类中方法。

wait方法

当一个线程调用一个共享变量的wait()方法时, 该调用线程会被阻塞挂起, 直到发生下面几件事情之一才返回:
(1) 其他线程调用了该共享对象的 notify()或者notifyAll()方法;
(2) 其他线程调用了该线程的 interrupt()方法, 该线程抛出InterruptedException异常返回。

另外,如果调用 wait()方法的线程没有事先获取该对象的监视器锁,则调用wait()方法时调用线程会抛出IllegalMonitorStateException异常。

那么,一个线程如何才能获取一个共享变量的监视器锁呢?
(1) 执行 synchronized同步代码块时,使用该共享变量作为参数。

synchronized(共享变量){so something
}

(2) 调用该共享变量的方法,并且该方法使用了synchronized修饰 。

synchronized void add(int a, int b){so something
}

notify方法

一个线程调用共享对象的 notify() 方法后,会唤醒一个在该共享变量上调用 wait系列方法后被挂起的线程。 一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的。

此外,被唤醒的线程不能马上从 wait 方法返回并继续执行,它必须在获取了共享对象的监视器锁后才可以返回,也就是唤醒它的线程释放了共享变量上的监视器锁后,被唤醒的线程也不一定会获取到共享对象的监视器锁,这是因为该线程还需要和其他线程一起竞争, 只有该线程竞争到了共享变量的监视器锁后才可以继续执行。

类似 wait 系列方法,只有当前线程获取到了共享变量的监视器锁后,才可以调用共享变量的 notify() 方法,否则会抛出 IllegalMonitorStateException异常。为了避免此类异常,wait与notify系列方法一般在共享代码块中调用。

notifyAll() 方法则会唤醒所有在该共享变量上由于调用 wait 系列方法而被挂起的线程。

生产者与消费者代码示例

public static void main(String[] args){ArrayDeque<Integer> deque = new ArrayDeque<>(20);Runnable producer = () -> {while (true) {synchronized (deque) {// 这里用while判断防止线程虚假唤醒while (deque.size() == 20) {try {System.out.println("producer wait");deque.wait();} catch (InterruptedException e) {e.printStackTrace();}}Random r = new Random();deque.add(r.nextInt(100));deque.notifyAll(); //通知唤醒消费者线程,有新元素可以消费了}}};Runnable consumer = () -> {while (true) {synchronized (deque) {while (deque.size() == 0) {try {System.out.println("consumer wait");deque.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("deque remove:" + deque.remove());deque.notifyAll();}}};new Thread(producer).start();new Thread(consumer).start();Thread.sleep(5000L);}

常用Thread类方法

join方法

join是无参且返回值为void的方法,用于等待某一线程执行完成后再向下执行。

线程 A 调用线程 B 的 join 方法后会被阻塞 , 当其他线程调用了线程 A 的
interrupt()方法中断了线程 A 时,线程 A 会抛出 InterruptedException 异常而返回。

public static void main(String[] args){Thread mainTread = Thread.currentThread();Runnable r1 = () -> {while (true) {}};Runnable r2 = () -> {try {Thread.sleep(3000L);mainTread.interrupt(); //注意这里中断的是主线程} catch (InterruptedException e) {e.printStackTrace();}};Thread thread1 = new Thread(r1);Thread thread2 = new Thread(r2);thread1.start();thread2.start();try {thread1.join(); //等待线程1结束,主线程阻塞} catch (InterruptedException e) {e.printStackTrace();System.out.println("thread is interrupted");}
}

interrupt方法

设置线程的中断位状态为true,不代表线程马上中断,而是被中断的线程根据中断状态自行处理 。通常,线程由于调用wait()、join()、sleep()而阻塞时,如果调用此方法会系统会抛出InterruptedException异常。

thread对象.isInterrupted() 是对象方法,返回某线程中断标志位状态。

Thread.interrupted() 是静态方法,返回当前线程中断标志位状态,同时清除标志位(置为false)。

sleep方法

Thread类中有一个静态的 sleep方法,当一个执行中的线程调用了 Thread的 sleep方法后,调用线程会暂时让出指定时间的执行权,也就是在这期间不参与 CPU 的调度,但是该线程所拥有的监视器资源,比如锁还是持有不让出的

指定的睡眠时间到了后该函数会正常返回,线程就处于就绪状态,然后参与 CPU 的调度,获取到 CPU 资源后就可以继 续运行了。如果在睡眠期间其他线程调用了该线程的 interrupt()方法中断了该线程,则该 线程会在调用 sleep 方法的地方抛出 IntermptedException 异常而返回。

wait方法会挂起线程同时让出监视器资源,需要notify()唤醒,这是和sleep的主要区别。

yield方法

Thread类中有一个静态的yield方法,当一个线程调用 yield方法时,实际就是在暗示线程调度器当前线程请求让出自己的CPU使用,但是线程调度器可以无条件忽略这个暗示。

当一个线程调用 yield 方法时, 当前线程会让出 CPU 使用权,然后处于就绪状态,线程调度器会从线程就绪队列里面获取一个线程优先级最高的线程,当然也有可能会调度到 刚刚让出CPU的那个线程来获取CPU执行权。

守护线程与用户线程

Java 中的线程分为两类,分别为daemon线程(守护线程)和user线程(用户线程)。 在JVM启动时会调用main函数, main函数所在的钱程就是一个用户线程,其实在JVM内部同时还启动了好多守护线程, 比如垃圾回收线程。那么守护线程和用户线程有什么区别呢? 区别之一是当最后一个非守护线程结束时, JVM会正常退出,而不管当前是否有守护线程 ,也就是说守护线程是否结束并不影响JVM的退出。 言外之意,只要有一个用
户线程还没结束,正常情况下JVM就不会退出。

JUC系列1-基础知识相关推荐

  1. 经典考题回顾系列——证券市场基础知识180题

    经典考题回顾系列--证券市场基础知识180题 [中华证券学习网]http://www.1000zq.com/Detail.aspx?id=180022 内容介绍>> 证券从业资格考试经典考 ...

  2. 硬件安全系列 逻辑电路基础知识介绍(一)

    前言 我带着新的系列又来了,之前的自动化代码审计工具还会在之后分享实际编写的过程思路以及代码. 新的系列是硬件安全,非常庞大的知识体系,我只是分享部分我所学习到的.会包括VLSI Testing ,H ...

  3. UICC 之 USIM 详解全系列——UICC基础知识介绍

    本人就职于国际知名终端厂商,负责modem芯片研发. 在5G早期负责终端数据业务层.核心网相关的开发工作,目前牵头6G算力网络技术标准研究. UICC 之 USIM 详解全系列--UICC(TS102 ...

  4. 【硬件设备】CPU系列之基础知识

    ​目录 概述 指令集 一.复杂指令集(CISC) 二.精简指令集(RISC) 三.复杂指令集与精简指令集区别 CPU的架构 一.X86 CPU架构 二.ARM CPU架构 三.MIPS CPU架构 四 ...

  5. 大数据系列sql基础知识(史上最全,收藏起来)

    大数据系列文章,从技术能力.业务基础.分析思维三大板块来呈现,你将收获: 1. 提升自信心,自如应对面试,顺利拿到实习岗位或offer: 2.掌握大数据的基础知识,与其他同事沟通无障碍: 3. 具备一 ...

  6. 跳槽者、应届生必看JAVA面试题系列 - JAVA基础知识(四)

    一: 前言 莫等闲,白了少年头,空悲切. 二: 面试挑战   在文章开始前,首先安利下"面试挑战": 凡是满足下面的挑战条件的,如果一个月内没有拿到一个Offer的,免费提供简历封 ...

  7. 硬件安全系列 逻辑电路基础知识介绍(三)

    前言 这一篇是逻辑电路基础知识的最后一篇. Don't Care Don't Care 可以称作冗余,在电子电路中,他有不同的类型对应不同的表现形式. 首先,我们从例子的角度探索一下冗余是怎么产生的. ...

  8. 【深度学习系列】基础知识、模型学习

    基础知识 原创 [深度学习]--训练过程 原创 [深度学习]--BN层(batch normalization) 原创 [深度学习]--激活函数(sigmoid.tanh.relu.softmax) ...

  9. java定义一个eat方法_小黄鸭系列java基础知识 | java中的方法

    前言 今天我们要探讨的问题,是java基础语法的最后一个问题,也就是java中的方法,今天主要从以下几个方面来介绍: 方法是什么(定义) 方法的分类 方法的调用 应该说,学完今天的知识,你至少应该看懂 ...

最新文章

  1. 网站地图能给网站的优化带来什么好处
  2. Openstack安装过程中出现的一些问题及解决
  3. 翻看雷军近10年演讲、采访,我们整理出70条干货
  4. elasticsearch 集群no known master node
  5. Kotlin实战指南九:延迟初始化
  6. Mahout各种推荐器的主要特点(转)
  7. word项目符号或编号bullets and numbering
  8. kong 网关日志格式修改
  9. 【JavaScript】JS事件机制学习
  10. c语言中二次规划函数是哪个好,c语言程序设计规划模拟试题二(含答案).doc
  11. 使用简介EntityFramework6.0
  12. Zuul 2 –样本过滤器
  13. 在Windows中为文件添加“可执行”权限(chmod +x 文件名 不起作用)
  14. xss绕过字符过滤_XSS绕过实战练习
  15. 吴军:为什么计算机不是万能的
  16. 何小鹏:创业初期每月都去借钱 网易丁磊借了80万
  17. 95-190-040-源码-window-Session Window
  18. nginx 发送动态内容注意事项
  19. IOS程序之发送短信代码实现
  20. 概率论与数理统计(第四版) 课后习题解析 盛骤、谢式千 编|高等教育出版社 大学课后习题答案

热门文章

  1. contos8安装jenkins
  2. 无参考图像清晰度评价
  3. webstorm总是闪退
  4. 蓝牙比较常分析的东西
  5. 微信小程序中实现一段文字当只有一行时居中显示,多行时左对齐
  6. 华胜天成网络智能备份系统解决方案
  7. MySQL varchar类型的比较
  8. MATLAB实现矩阵的乘法
  9. 计算机网络的体系结构-各层需要解决的问题
  10. L2-L4自动驾驶视觉方案推荐(一)