Java多线程知识整理
多线程
1. 多线程基础
多线程状态转换图
普通方法介绍
yeild
yeild,线程让步。是当前线程执行完后所有线程又统一回到同一起跑线。让自己或者其他线程运行,并不是单纯的让给其他线程。
join
等待线程结束;调用线程等待当前线程结束后才能往下执行,阻塞线程之意。join本质是在当前对象实例上调用线程wait()
如下所示:输出完 thread-1后再输出end
public static void main(String[] args) throws InterruptedException{
System.out.println("main start");
Thread t1 = new Thread(new Worker("thread-1"));
t1.start();
t1.join();
System.out.println("main end");
}
sychronized
sychronized确保安全外,还能保证线程之间的可见性和有序性
- 指定加锁对象:对给定的对象加锁,进入同步代码前要先获得给定对象的锁
- 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要先获得当前实例的锁。
- 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要先获得当前类的锁。
2. JDK并发包
重入锁(ReentrantLock)
决定了线程是否可以访问临界区资源。object.wait()和object.notify()起到线程等待和通知的作用。这些工具能实现多线程相互之间的协作作用
sychronized功能的扩展-重入锁
重入锁:
重入锁使用ReentrantLock
来实现
public class ReentrantLocker implements Runnable {
public static ReentrantLock locker = new ReentrantLock();
private static int i = 0;
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
locker.lock();
try {
i++;
} catch (Exception e) {
e.printStackTrace();
}finally {
locker.unlock();
}
}
}
public static void main(String[] args) throws Exception{
ReentrantLocker locker1 = new ReentrantLocker();
// 对同一个对象加锁。important
Thread t1 = new Thread(locker1);
Thread t2 = new Thread(locker1);
t1.start();
t2.start();
/**调用join方法,阻塞当前线程,待所有线程执行完再往下执行*/
t1.join();
t2.join();
System.out.println(i);
}
}
重入锁对比sychronized有点
- 中断响应
对于sychronized来说,一个线程要访问被同步的资源,要么继续执行,要么继续等待。而使用重入锁,线程可以被中断。程序可以取消对锁的请求,避免死锁的发生。 - 锁超时设置
//表示线程对资源锁等待时长。
//如果在规定的时间内没有获取到锁,
//则线程获取锁失败
lock.tryLock(5, TimeUnits.SECONDS)
如果tryLock
没加参数,就不需要等待。
3. 公平性
公平锁会按照时间先后顺序,保证先到先得,后到者后得。公平锁的一大特点是:不会产生饥饿现象,只要你排队,最后还是能得到资源的
重入锁条件-Condition
信号量(Semaphore)
信号量是对锁的扩展,无论是内部锁sychronized还是重入锁ReentrantLock,一次都只允许一个线程访问一个资源,而信号量指定多个线程同时访问同一资源
- 构造函数
public Semaphore(int permits);
public Semahore(int permits, boolean fair);
构造信号量对象时,permits
参数表示的是:同时多少个线程能访问同一资源。
读写锁(ReadWriteLock)
读写分离锁可以有效的较少锁竞争,用锁分离机制来提升系统性能。
读写锁的访问约束情况
-- | 读 | 写 |
---|---|---|
读 | 非阻塞 | 阻塞 |
写 | 阻塞 | 阻塞 |
倒计时器(CountDownLatch)
可以让某一线程等待直到倒计时结束,再开始执行。当调用wait
方法的时候,主线程被阻塞,直到countdown减到0的时候,程序再往下执行。
循环栅栏(CyclicBarrier)
作用是阻止线程继续执行,要求线程在栅栏处等待,计数器可以循环使用。假如将计数器设置为10,那么凑齐第一批10个进程数后计数器会归为0,然后接着凑齐下一批10个线程
线程池
声明一个线程池
线程池的作用是复用线程,减少系统开销。声明一个线程池有如下方法
/**
该方法返回一个固定线程数量的线程池。
当有一个新的任务提交,线程池有空闲的线程就提交,没有空闲的话就缓存到待执行列表中,
直到有空闲的线程才执行
*/
public ExecutorService newFixedThreadService(int threadNum);
/**
该方法值返回包含一个线程的线程池,
如果有多个任务提交也是将多余的任务保存到一个任务队列中,待有空闲的线程才执行
**/
public ExecutorService newSingleThreadExecutor();
/**
返回可根据实际情况调整线程数量的线程池,线程池中的线程数量是不一定的,但还是能够复用空闲线程的
*/
public ExecutorService newCachedThreadPool();
线程池的内部实现
通过源代码可以看出newFixedThreadService
,newSingleThreadExecutor
,newCachedThreadPool
内部都是由ThreadPoolExecutor
来处理的
ThreadPoolExecutor的内部实现
public ThreadPoolExecutor(
// 线程池中线程数量
int corePoolSize,
// 线程池中允许线程最大数量
int maximumPoolSize,
// 线程池中线程数量超出corePoolSize的线程的存活时间
long keepAliveTime,
// 时间单位
TimeUnit unit,
// 任务队列,被提交尚未被执行的任务
BlockingQueue<Runnable> workQueue,
// 拒绝策略,任务太多来不及处理,如何拒绝任务
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
其中workqueue
是指那些尚未被执行的线程队列。它是BlockingQueue
的接口对象。在ThreadPoolExecutor
中可以使用如下几种 BlockQueue
- 直接提交的队列
- 有界的任务队列
- 无界任务队列
- 优先任务队列
ThreadLocal
为每个线程运行时存储所需参数。
如下是线程安全的日期格式化方法
public class SafeDataFormat {
static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>();
static Date date = new Date();
static String dateStr = "2019-04-27 04:12:41";
static class ParseDate implements Runnable {
@Override
public void run() {
if (null == simpleDateFormatThreadLocal.get()) {
simpleDateFormatThreadLocal.set(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
}
SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get();
try {
System.out.println(simpleDateFormat.parse(dateStr));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 2000; i++) {
executorService.execute(new ParseDate());
}
executorService.shutdown();
}
}
ThreadLocal实现原理
先看ThreadLocal的get(), set()方法。
set方法
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
从代码中可以看出首先获取当前线程对象,然后通过getMap()拿到当前线程的ThreadLocalMap,并将值更新到这个map中。
get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
首先get()获取到当前对象的ThreadLocalMap
对象,然后将当前线程作为key来获取内部的实际数据。
这些变量是维护在Thread内部的(ThreadLocalMap定义所在类),意味着只要线程不退出,对象的引用就一直都存在着。
ThreadLocalMap的实现使用了弱引用,java虚拟机在进行垃圾回收时,如果发现变量是弱引用,就会立即回收
CAS 比较交换 CompareAndSweep
由于其的非阻塞性,它对死锁天生免疫。
CAS的算法过程
包含三个参数CAS(V, E, N)。V表示要更新的变量,E表示预期值,N表示新值。仅当 V = E时,才会将V设置成N;如果V值跟E值不同,则说明已经被其他线程醉了更新,当前线程什么都不做,最后CAS返回当前V的真实值
CAS操作是抱着乐观的态度进行的,它总认为自己可以完成操作。当多个线程同时CAS同一个值额时候,只有一个线程会胜出并成功更新。失败的线程不会被挂起,仅是被告知操作失败,并允许再次尝试
简单的说,CAS需要额外给出期望值,也就是你认为这个变量最终的样子。如果这个变量最终跟你的预期不同,你就重新读取再次更新就好了。
AtomicInteger
是可变、线程安全的。基于CAS的;就内部实现来说,AtomicIntger保存了核心字段
//代表了当前AtomicInteger的实际值
private volatile int value;
// 保存着value字段在AtomicInteger对象的偏移量。实现AtomicInteger的关键
private static final long valueOffset;
关注一下incrementAndGet()
public final int incrementAndGet() ;
for(; ; )
int v current = get();
int next = cueernt + 1;
if (compareAndSet(current, next))
return next
CAS操作未必是成功的,因此对于不成功的情况,要不断的去重试
Java多线程知识整理的更多相关文章
- JAVA多线程知识总结(二)
本文是承接上一篇文章:JAVA多线程知识总结(一) 四.Java多线程的阻塞状态与线程控制 上文已经提到线程阻塞的集中具体类型.下面主要看引起JAVA线程阻塞的方法 1,join()-----让一个 ...
- 2019-9-16 java上课知识整理总结(动手动脑,课后实验)
java上课知识整理总结(动手动脑,课后实验) 一,课堂测试 1,题目:课堂测试:像二柱子那样,花二十分钟写一个能自动生成30道小学四则运算题目的 “软件” 要求:(1)题目避免重复: (2)可定制( ...
- JAVA 多线程知识总结(一)
一,线程的生命周期以及五种基本状态 关于JAVA线程的生命周期,首先看一下下面这张图 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌握了. Ja ...
- 阿里 P8 高级架构师吐血总结的 《Java 核心知识整理&面试.pdf》| 免费分享
最近在网上发现一份非常棒的 PDF 资料,据说是阿里 P8 级高级架构师吐血总结的, 其中内容覆盖很广,包括 Java 核心基础.Java 多线程.高并发.Spring.微服务.Netty 与 RPC ...
- Java并发知识整理
整理了一下前段时间学习Java并发的笔记,大约有40篇. 1. Java并发基础知识 并发基础(一) 线程介绍 并发基础(二) Thread类的API总结 并发基础(三) java线程优先级 并发基础 ...
- JAVA hashmap知识整理
HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题.HashMap的工作原理.ArrayList与Vect ...
- 面试求职中需要了解的Java多线程知识
Java中多线程的实现方式 在java的历史版本中,有两种创建多线程程序的方法 1) 通过创建Thread类的子类来实现(Thread类提供了主线程调用其它线程并行运行的机制) 主要步骤: 自定义类继 ...
- java基础知识整理
java基础入门知识(转载请注明出处.) 1.JVM.JRE和JDK的区别. (1)JVM(Java Virtual Machine):java虚拟机,用于保证java跨平台的特性,java语言是跨平 ...
- Java多线程知识总结(一)
一.创建线程的三种方式: 创建线程的方式有三种,一是创建Thread实例,二是实现Runnable接口,三是实现Callable接口,Runnable接口和Callable接口的区别是一个无返回值,一 ...
随机推荐
- Promise原理—一步一步实现一个Promise
promise特点 一个promise的当前状态只能是pending.fulfilled和rejected三种之一.状态改变只能是pending到fulfilled或者pending到rejected ...
- djiango的模板语言(template)
老师的博客:http://www.cnblogs.com/liwenzhou/p/7931828.html 官方文档:https://docs.djangoproject.com/en/1.11/re ...
- JS 图片放大镜
今天练习一个小demo, 从本地读取图片, 然后实现类似淘宝放大镜的效果, 再加两个需求 1 .可以调节缩放比例,默认放大两倍 2 . 图片宽高自适应, 不固定宽高 话不多说先看效果: 原理:1, 右 ...
- Vue 进阶之路(四)
之前的文章我们已经对 vue 有了初步认识,这篇文章我们通过一个例子说一下 vue 的样式绑定. 现在我们想要是想这样一个需求,页面上有个单词,当我们点击它的时候颜色变为红色,再点击一次变为原来的颜色 ...
- 机器学习——KMeans聚类,KMeans原理,参数详解
0.聚类 聚类就是对大量的未知标注的数据集,按数据的内在相似性将数据集划分为多个类别,使类别内的数据相似度较大而类别间的数据相似度较小,聚类属于无监督的学习方法. 1.内在相似性的度量 聚类是根据数据 ...
- ConcurrentDictionary并发字典知多少?
背景 在上一篇文章你真的了解字典吗?一文中我介绍了Hash Function和字典的工作的基本原理. 有网友在文章底部评论,说我的Remove和Add方法没有考虑线程安全问题. https://doc ...
- css样式的继承性、层叠性 、优先级
一.css样式的继承性: 作用:给父元素设置一些属性,子元素也可以使用 应用场景: 一般用于设置网页上的一些共性信息,例如网页的文字颜色,字体,文字大小等内容.优化代码,降低工作量 注意点: 1.并不 ...
- ArcGIS对SLD样式的支持
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.ArcGISWMS服务对SLD的支持 在完成用geoserver的w ...
- 2018-01-12 Antlr4添加中文变量赋求值,括号,各种问题
中文编程知乎专栏原文地址 例程(更多测试用例在此): 基数=100 基数×(基数+1)÷2 => 求值为5050 续上文Antlr4实现数学四则运算, 修改的语法规则部分: 程序: 声明+; 声 ...
- Linux记录~持续更新~
ls -ildha /etc -i 显示对应id号 唯一标识 -l 显示详情 -d 显示当前文件夹 不包括子目录 -h 单位为KB 而不是B -a 显示所有 包括隐藏文件 mkdir mkdir -p ...