Java并发编程基础

线程的简介

什么是线程

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

为什么要使用多线程

目前的处理器核心越来越多,使用多线程能有更快的响应时间,并能有更好的编程模型。

线程优先级

现代操作系统基本采用时分的形式调度运行的线程,操作系统分出每一个时间片会根据线程的优先级来分配,优先级越高的最先获取执行资源。

在Java线程中,通过一个整型成员变量priority来控制优先级,优先级的范围从1~10,在线程构建的时候可以通过setPriority(int)方法来修改优先级,默认优先级是5,优先级高的线程分配时间片的数量要多于优先级低的线程。

线程优先级的设置:

	频繁阻塞(休眠或者I/O操作)的线程需要设置 较高优先级,偏重计算(需要较多CPU时间或者偏运算)的线程则设置 较低的优先级,确保处理器不会被独占。

在不同的 JVM 以及 操作系统 上,线程规划会存在差异,有些操作系统甚至会忽略对线程优先级的设定。

线程优先级不能作为程序正确性的依赖,因为操作系统可以完全不用理会 Java 线程对于优先级的设定。

线程的状态

	NEW 初始状态RUNNABLE 运行状态BLOCKED 阻塞状态WAITING 等待状态TIME_WAITING 超时等待状态TERMINATED 终止状态

下图是状态变化的介绍:
在这里插入图片描述

Daemon线程

Daemon 线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。
这意味着,当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出(Daemon线程不一定会执行完)。

可以通过调用Thread.setDaemon(true)将线程设置为Daemon线程。需在启动之前设置。

启动和终止线程

线程随着 thread.start() 开始启动 到 run() 方法执行完毕 结束。

我们可以通过 Thread.interrupted() 方法中断线程。

	中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。线程通过检查自身是否被中断来进行响应,线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位。如果该线程已经处于终结状态,即使该线程被中断过,在调用该线程对象的isInterrupted()时依旧会返回false。许多声明抛出InterruptedException的方法(例如Thread.sleep(long millis)方法)这些方法在抛出InterruptedException之前,Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException,此时调用isInterrupted()方法将会返回false。

示例

public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (true) {try {TimeUnit.SECONDS.sleep(10);System.out.println("time = " + System.currentTimeMillis() / 1000 + ", i = " + i);} catch (InterruptedException e) {e.printStackTrace();}}}, "t1");Thread t2 = new Thread(() -> {while (true) {i++;}}, "t2");//设置为 daemon 线程 并启动t1.setDaemon(true);t2.setDaemon(true);t1.start();t2.start();//让t1 t2 运行3sTimeUnit.SECONDS.sleep(3);//中断线程t1.interrupt();t2.interrupt();//获取中断状态System.out.println("time = " + System.currentTimeMillis() / 1000 + ", t1.isInterrupted() = " + t1.isInterrupted());System.out.println("time = " + System.currentTimeMillis() / 1000 + ", t2.isInterrupted() = " + t2.isInterrupted());//防止 t1 t2 立即退出TimeUnit.SECONDS.sleep(15);
}

输出

time = 1560134045, t1.isInterrupted() = false
time = 1560134045, t2.isInterrupted() = true
java.lang.InterruptedException: sleep interruptedat java.lang.Thread.sleep(Native Method)at java.lang.Thread.sleep(Thread.java:340)at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)at com.tcl.executors.Test.lambda$main$0(Test.java:16)at java.lang.Thread.run(Thread.java:745)
time = 1560134055, i = -576615207

根据输出结果,我们知道在线程sleep的时候,调用 isInterrupted() 会导致 sleep interrupted 异常,并且中断标记也被清除了。

已经被废弃的 suspend()(暂停)、resume()(恢复) 和 stop()(停止)。
废弃原因是,在调用方法之后,线程不会保证占用的资源被正常释放。

示例:

public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (true) {System.out.println("time = " + System.currentTimeMillis() / 1000);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}});t.setDaemon(true);t.start();TimeUnit.SECONDS.sleep(3);t.suspend();System.out.println("suspend time = " + System.currentTimeMillis() / 1000);TimeUnit.SECONDS.sleep(3);t.resume();System.out.println("resume time = " + System.currentTimeMillis() / 1000);TimeUnit.SECONDS.sleep(3);t.stop();System.out.println("stop time = " + System.currentTimeMillis() / 1000);TimeUnit.SECONDS.sleep(3);
}

输出

time = 1560134529
time = 1560134530
time = 1560134531
suspend time = 1560134532
resume time = 1560134535
time = 1560134535
time = 1560134536
time = 1560134537
stop time = 1560134538

线程间通信

volatile和synchronized关键字

volatile修饰的变量,程序访问时都需要在共享内存中去读取,对它的改变也必须更新共享内存,保证了线程对变量访问的可见性。

synchronized:对于 同步块 的实现使用了monitorenter和monitorexit指令,而 同步方法 则是依靠方法修饰符上的ACC_SYNCHRONIZED来完成的。无论采用哪种方式,其本质是对一个对象的监视器monitor进行获取,而这个获取过程是排他的,也就是同一时刻只能有一个线程获取到由synchronized所保护对象的监视器。

等待/通知机制——wait和notify

指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。

等待:wait()、wait(long)、wait(long, int)

通知:notify()、notifyAll()

示例

private static Object object = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);synchronized (object) {System.out.println("t1 start object.wait(), time = " + System.currentTimeMillis() / 1000);object.wait();System.out.println("t1 after object.wait(), time = " + System.currentTimeMillis() / 1000);}} catch (InterruptedException e) {e.printStackTrace();}});Thread t2 = new Thread(() -> {try {TimeUnit.SECONDS.sleep(5);synchronized (object) {System.out.println("t2 start object.notify(), time = " + System.currentTimeMillis() / 1000);object.notify();System.out.println("t2 after object.notify(), time = " + System.currentTimeMillis() / 1000);}synchronized (object) {System.out.println("t2  hold lock again, time = " + System.currentTimeMillis() / 1000);}} catch (InterruptedException e) {e.printStackTrace();}});t1.start();t2.start();
}

输出

t1 start object.wait(), time = 1560138112
t2 start object.notify(), time = 1560138116
t2 after object.notify(), time = 1560138116
t2  hold lock again, time = 1560138116
t1 after object.wait(), time = 1560138116

1.使用wait()、notify()和notifyAll()时需要先对调用对象加锁,否则会报java.lang.IllegalMonitorStateException异常。

2.调用wait()方法后,线程状态由RUNNING变为WAITING,并将当前线程放置到对象的等待队列。

3.notify()或notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()或notifAll()的线程释放锁之后,等待线程才有机会从wait()返回。

4.notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll()方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程状态由WAITING变为BLOCKED。

5.从wait()方法返回的前提是获得了调用对象的锁。

等待/通知的经典范式

包括 等待方(消费者)和 通知方(生产者)。

等待方遵循以下原则:

	获取对象的锁。如果条件不满足,那么调用对象的wait方法,被通知后任要检查条件。条件不满足则执行对应的逻辑。

示例

synchronized (对象) {while (条件不满足) {对象.wait();}对应的处理逻辑
}

通知方遵循以下原则:

	获取对象的锁。改变条件。通知所有在等待在对象上的线程。
synchronized (对象) {改变条件对象.notifyAll();
}

管道输入/输出流

PipedOutputStream、PipedInputStream、PipedReader 和 PipedWriter。

示例

private static PipedWriter writer;
private static PipedReader reader;public static void main(String[] args) throws InterruptedException, IOException {writer = new PipedWriter();reader = new PipedReader();//绑定输入输出writer.connect(reader);Thread t = new Thread(() -> {int res;try {while ((res = reader.read()) != -1) {System.out.print((char) res);}} catch (Exception e) {e.printStackTrace();}});t.start();int res;while ((res = System.in.read()) != -1) {System.out.println(res);writer.write(res);//按回车结束if (res == 10) {break;}}writer.close();
}

输出

Hi!
72
105
33
Hi!

Thread.join()

thread.join() 即当前线程需要在 thread 线程执行完之后才能继续执行,Java Thread.join()详解,这里已经做了详细介绍了,就不再赘述了。

ThreadLocal

ThreadLocal,即线程变量,是一个以ThreadLocal对象为 键 、任意对象 为 值 的存储结构。
这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。

可以通过 set(T t) 设置, get() 获取。

示例

private static ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) throws InterruptedException {String time = String.valueOf(System.currentTimeMillis() / 1000);System.out.println("time = " + time);threadLocal.set(time);Thread t = new Thread(() -> {String time1 = String.valueOf(System.currentTimeMillis());System.out.println("time1 = " + time1);threadLocal.set(time1);});t.start();TimeUnit.SECONDS.sleep(5);System.out.println("threadLocal.get() = " + threadLocal.get());
}

输出

time = 1560146178
time1 = 1560146178263
threadLocal.get() = 1560146178

可以看到线程t中对threadLocal设置的值,并不影响main线程中的值。
set(T value)方法的源代码:

可以看到即把 当前ThreadLocal对象 为 key,传入的参数 为value 保存在 ThreadLocalMap中。

public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);
}

欢迎大家点赞关注哟!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/565334.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【OpenCV 例程200篇】236. 特征提取之主成分分析(OpenCV)

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】236. 特征提取之主成分分析&#xff08;OpenCV&#xff09; 文章目录【youcans 的 OpenCV 例程200篇】236. 特征提取之主成分分析&#xff08;OpenCV&#xff09;5.2 主成分分析的数学方法5.4 OpenC…

linux 访问共享内存,Linux下的共享内存(03)---通过指针访问共享内存中的数据...

环境&#xff1a;Vmware Workstation&#xff1b;CentOS-6.4-x86_64说明&#xff1a;1、将共享内存挂载至进程&#xff1a;void *shmat(int shmid, const void *shmaddr,int shmflg);参数shmid是要附加的共享内存区标示符。总是把参数shmaddr设为0。参数shmflg可以为SHM_RDON…

Java中锁的使用和实现

首先&#xff0c;我们要了解一个概念&#xff0c;JAVA中的锁到底是什么呢&#xff1f; 锁是用来控制多个线程访问共享资源的方式&#xff0c;一般来说&#xff0c;一个锁能够防止多个线程同时访问共享资源。 Lock接口 在Java SE 5之后&#xff0c;并发包中新增了Lock接口&am…

【OpenCV 例程200篇】225. 特征提取之傅里叶描述子

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】225. 特征提取之傅里叶描述子 目标特征的基本概念 通过图像分割获得多个区域&#xff0c;得到区域内的像素集合或区域边界像素集合。我们把感兴趣的人或物称为目标&#xff0c;目标所处的区域就是目…

【OpenCV 例程300篇】238. OpenCV 中的 Harris 角点检测

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】238. OpenCV 中的 Harris 角点检测 角是直线方向的快速变化。角点通常被定义为两条边的交点&#xff0c;或者说角点的邻域应该具有两个不同区域的不同方向的边界。 角是高度有效的特征。角点检测&…

基础线性规划实现(matlab,lingo)

目录 一、本次所需解的问题 二、matlab解题 1&#xff09;语法 2&#xff09;数学思维 3&#xff09;matlab解题 运行结果&#xff1a; 三、lingo解题 lingo解题如下&#xff1a; 运行结果&#xff1a; 最后&#xff1a; 一、本次所需解的问题 需解出下面该线性规划问…

cwntos linux kde桌面,Centos如何安装KDE的桌面

其实KDE其实在初次安装系统的时候就可以选择&#xff0c;我下面是进入系统后的安装方法&#xff1a;第一步&#xff1a;检查KDE首先查看自己是否安装了KDE# yum grouplist在grouplist的输出结果中的“Installed Groups:”部分中&#xff0c;如果你能找到“X Window System”和“…

【OpenCV 例程 300篇】239. Harris 角点检测之精确定位(cornerSubPix)

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】239. Harris 角点检测之精确定位&#xff08;cornerSubPix&#xff09; 角是直线方向的快速变化。角点通常被定义为两条边的交点&#xff0c;或者说角点的邻域应该具有两个不同区域的不同方向的边界…

基础线性规划实现---python

目录 一、问题 何为线性规划问题&#xff1a; 二、python进行求解 1.通过观察matlab解线性规划步骤进行求解 2.python求解步骤 1&#xff09;求解用到的模块&#xff08;scipy 和 numpy&#xff09;&#xff1a; 2&#xff09;对 max z2x13x2-5x3 该问题确定c如下&…

【OpenCV 例程 300篇】240. OpenCV 中的 Shi-Tomas 角点检测

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】240. OpenCV 中的 Shi-Tomas 角点检测 角是直线方向的快速变化。角点通常被定义为两条边的交点&#xff0c;或者说角点的邻域应该具有两个不同区域的不同方向的边界。 角是高度有效的特征。角点检测…

多元统计分析1

第一章 多元正态分布 文章目录 1.1 多元分布的基本概念 1.1.1 随机向量 1.1.2 分布函数与密度函数 联合分布函数&#xff1a; 联合密度函数&#xff1a; 条件密度函数&#xff1a; 分量的独立性&#xff1a; 1.1.3 随机向量的数字特征 1.随机向量的均值 2、随机…

Java并发容器和框架

ConcurrentHashMap 我们为什么要使用 ConcurrentHashMap呢&#xff1f; 原因有三&#xff1a; 并发编程中HashMap会导致死循环&#xff1b;HashTable效率又非常低&#xff1b;ConcurrentHashMap的锁分段技术可有效提升并发访问率。在并发编程使用HashMap会导致死循环。 在多线…

【OpenCV 例程 300篇】241. 尺度不变特征变换(SIFT)

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】241. 尺度不变特征变换&#xff08;SIFT&#xff09; 6.4.1 简介 尺度不变特征转换算法&#xff08;Scale-invariant feature transform&#xff0c;SIFT&#xff09;是图像处理中经典的局部特征描…

整数线性规划实现(matlab分枝界定法)

文章目录 一、本次问题 1.利用第一天所学知识求解&#xff1a; 2.本题理解&#xff1a; &#xff08;1&#xff09;分支界定法 背景&#xff1a; 基本理论&#xff08;解题步骤&#xff09;&#xff1a; 求解实现1&#xff1a; 1.第一步 2.第二步 3.第三步 4.第四步…

linux opencv gtk 没窗口,OpenCV GTK+2.x error

可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效&#xff0c;请关闭广告屏蔽插件后再试):问题:I had installed OpenCV following these steps ().After trying to compile one examples,i got this error :OpenCV Error: Unspecified error (The function …

硬盘分区

我们买回一台全新的笔记本的时候&#xff0c;我们会发现里面只有一个硬盘&#xff0c;这个时候为了满足我们的需求我们往往要对硬盘进行分区&#xff0c;那麽如何正确的对硬盘进行分区的呢&#xff1f; 下面教大家如何正确的对电脑硬盘进行分区操作。 操作方法 1、在桌面上找…

python入门:Anaconda和Jupyter notebook的安装与使用

文章目录 一、安装和使用Anaconda 1、anaconda是什么&#xff1f; 2、为什么需要安装anaconda&#xff1f; 3、如何安装anaconda&#xff1f; 通过官网下载页面 开源软件下载 安装步骤&#xff1a; 4、jupyter汉化 5、如何管理包&#xff1f; 1.列出已安装的包 2.安装…

非线性规划(1)

目录 一、非线性规划的定义 二、非线性规划的模型 三、非线性规划函数 四、线性不等式约束 五、线性不等式和等式约束 六、带有非线性约束的求最值 七、非线性约束 总结&#xff1a; 一、非线性规划的定义 前面我们学了线性规划&#xff0c;整数规划&#xff0c;我们可…

响应式布局Demo

顾名思义&#xff0c;响应式布局就是为适应不同终端而形成的一种技术。我总结了一个简单的例子帮助大家了解和学习响应式布局。 实现响应式布局的几种方式&#xff1a;媒体查询、JS、流体布局、弹性布局… 常用的meta标签 <meta content"widthdevice-width,initial-…

整数线性规划实现(lingo,python分枝界定法)

本文章为上篇建模学习打卡第二天的续 文章目录 一、本次问题 二、本题理解 三、问题求解 1.lingo实现 &#xff08;1&#xff09;先抛除整数约束条件对问题求解 &#xff08;2&#xff09;加入整数约束条件求解 2.python实现求解 &#xff08;1&#xff09;先抛除整数约…