个人博客网:https://wushaopei.github.io/    (你想要这里多有)

一、死锁

1、死锁的定义

所谓的死锁是指两个或两个以上的线程在等待执行的过程中,因为竞争资源而造成的一种互相等待的现象。若不受外力作用,他们都将无法推进下去。此时,处于系统中所处的状态就是死锁。

2、发生死锁所必须具备的条件:

互斥条件:它是指进程对所分配的资源进行排他性的使用,在一定时间内,某资源只由一个进程在用,如果此时还有其他进程请求资源,请求者只能等待。直到占有资源的进程用完或释放之后才可以继续使用。

请求和保持条件:它是指进程已经保持了至少一个资源,又提出了新的资源请求,该资源已被其它进程占用。此时,请求进程阻塞,对自己或者其它资源保持不放;

不剥夺条件:指进程获取资源在用完之前不能被剥夺,只能在使用完后再由自己释放;

环路等待条件:发生死锁的时候,一定存在一个进程的资源是一个环形的链。

3、代码演示死锁:

/**
* 一个简单的死锁类
* 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒
* 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
* td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
* td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
* td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。
*/ @Slf4j
public class DeadLock implements Runnable {
public int flag = 1;
//静态对象是类的所有对象共享的
private static Object o1 = new Object(), o2 = new Object(); @Override
public void run() {
log.info("flag:{}", flag);
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o2) {
log.info("1");
}
}
}
if (flag == 0) {
synchronized (o2) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o1) {
log.info("0");
}
}
}
} public static void main(String[] args) {
DeadLock td1 = new DeadLock();
DeadLock td2 = new DeadLock();
td1.flag = 1;
td2.flag = 0;
//td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
//td2的run()可能在td1的run()之前运行
new Thread(td1).start();
new Thread(td2).start();
}
}

执行并打印结果:

21:13:12.979 [Thread-1] INFO com.mmall.concurrency.example.deadLock.DeadLock - flag:0
21:13:12.979 [Thread-0] INFO com.mmall.concurrency.example.deadLock.DeadLock - flag:1 Process finished with exit code 1

相关扩展:Java项目中使用log记录日志的一些总结

二、多线程并发最佳实践

1、使用本地变量:应该使用本地变量,而不是创建一个类或实例变量。通常情况下,开发人员使用对象实例作为一个类,可以节省内存,并可以重用。因为每次在方法中创建新的本地变量会消耗很多内存。

2、使用不可变类:如 String 、Integer等,一旦创建就不会改变了,不可变类可以降低代码中的同步数量

3、最小化锁的作用域范围: S=1/(1-a+a/n)

4、使用线程池的Executor,而不是直接new Thread执行:

创建一个线程的代价是昂贵的,如果你要得到一个可伸缩的java应用,那么你需要使用线程池,从线程池来管理线程,jdk中提供了各种方法实现

5、宁可使用同步也不要使用线程的wait和notify

从java1.5以后增加了许多的同步工具,要优先使用同步工具,而不是使用wait和notify方法

6、使用BlockingQueue实现生产-消费模式

7、使用并发集合而不是加了锁的同步集合

8、使用Semaphore创建有界的访问

9、宁可使用同步代码块,也不使用同步的方法

10、避免使用静态变量

三、Spring与线程安全

Spring bean  : singleton 、 prototype

无状态对象

无状态对象:就是自身没有状态的对象,当然也就不会因为多个线程交替调度破坏自身的状态而导致安全问题。无状态对象包括经常使用的DTO、VO,只作为数据实体的模型对象。

四、HashMap与ConcurrentHashMap解析

1、HashMap 的数据结构:

在java编程语言中,最基本的结构有两种,一个是数组,另外一个就是指针,即引用。

HashMap的底层就是一个数组结构,数组的每一项又是一个链表,当我们新建HashMap的时候就会初始化一个数组出来。

HashMap有两个参数影响它的性能,分别是初始容量加载因子。

由源码可知,HashMap的初始容量为16,

由上图中,可知HashMap的默认加载因子为0.75.

当HashMap 的长度达到的容量长度满足初始值的0.75时,就会调用resize()方法进行扩容:

我们也可以根据需要指定HashMap的初始化容量和加载因子。

2、HashMap 的线程安全性:

HashMap线程不安全的,主要体现在前面的resize()方法,它可能会导致死循环的发生,并且在使用迭代器的时候fasfree。当HashMap的长度超过了它的capacity * loadFactor时,就需要对它进行扩容,具体方法是:它要创建一个新的长度为原来容量的两倍的数组。它保证新的容量为2的N次方,从而保证寻址的方式依然适用。同时,它原来的数组会全部插入到新的数组中。这个过程我们称之为rehash。

这个方法并不保证线程安全,而且在多线程并发调用时可能陷入死循环。

3、HashMapd的ReHash 操作示意图:

单线程下的ReHash操作:

多线程下的ReHash操作:

4、ConcurrentHashMap的底层数据结构:

5、ConcurrentHashMap和HashMap的不同点:

  • ConcurrentHashMap是线程安全的,HashMap是线程不安全的;
  • HashMap允许key\value为空,而ConcurrentHashMap是不允许的

6、ConcurrentHashMap 改进后

Java7以后针对并发访问引入了Segment这个结构,实现了分段锁,提高并发度,与Segment的个数是相等的。Java8以后为了进一步提高并发性,它废弃了这里面的分段锁方案,并且直接使用一个大的数组,同时为了提高hash碰撞下的寻址做了性能优化。

Java8以后它的链表的长度超过一定的值(默认为8),这里的链表就会变成了红黑树。

Java并发编程 (十) 多线程并发拓展的更多相关文章

  1. java并发编程--第一章并发编程的挑战

    一.java并发编程的挑战 并发编程需要注意的问题: 并发编程的目的是让程序运行的更快,然而并不是启动更多的线程就能让程序最大限度的并发执行.若希望通过多线程并发让程序执行的更快,会受到如下问题的挑战 ...

  2. java并发编程与高并发解决方案

    下面是我对java并发编程与高并发解决方案的学习总结: 1.并发编程的基础 2.线程安全—可见性和有序性 3.线程安全—原子性 4.安全发布对象—单例模式 5.不可变对象 6.线程封闭 7.线程不安全 ...

  3. Java并发编程系列-(1) 并发编程基础

    1.并发编程基础 1.1 基本概念 CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运 ...

  4. Python并发编程04 /多线程、生产消费者模型、线程进程对比、线程的方法、线程join、守护线程、线程互斥锁

    Python并发编程04 /多线程.生产消费者模型.线程进程对比.线程的方法.线程join.守护线程.线程互斥锁 目录 Python并发编程04 /多线程.生产消费者模型.线程进程对比.线程的方法.线 ...

  5. 并发编程概述--C#并发编程经典实例

    优秀软件的一个关键特征就是具有并发性.过去的几十年,我们可以进行并发编程,但是难度很大.以前,并发性软件的编写.调试和维护都很难,这导致很多开发人员为图省事放弃了并发编程.新版.NET 中的程序库和语 ...

  6. Java并发编程、多线程、线程池…

    <实战java高并发程序设计>源码整理https://github.com/petercao/concurrent-programming/blob/master/README.md Ja ...

  7. Java并发编程之多线程

    线程 进程/线程/协程/管程 进程:操作系统会以进程为单位,分配系统资源(CPU时间片.内存等资源),是资源分配的最小单位 进程间通信(IPC): 管道(Pipe) 命名管道(FIFO) 消息队列(M ...

  8. Java并发编程,多线程[转]

    Java并发编程 转自:http://www.cnblogs.com/dolphin0520/category/602384.html 第一个例子(没有阻塞主线程,会先输出over): package ...

  9. 4、Java并发性和多线程-并发编程模型

    以下内容转自http://ifeve.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E6%A8%A1%E5%9E%8B/: 并发系统可以采用多种并发编程模型来实现. ...

随机推荐

  1. python 读取矢量文件

    #导入包 from osgeo import ogr #打开文件(False - read only, True - read/write) filename = "文件名.shp" ...

  2. 带你看看Java的锁(二)-Semaphore

    前言 简介 Semaphore 中文称信号量,它和ReentrantLock 有所区别,ReentrantLock是排他的,也就是只能允许一个线程拥有资源,Semaphore是共享的,它允许多个线程同 ...

  3. Mysql常用sql语句(九)- like 模糊查询

    测试必备的Mysql常用sql语句,每天敲一篇,每次敲三遍,每月一循环,全都可记住!! https://www.cnblogs.com/poloyy/category/1683347.html 前言 ...

  4. 计算机组成及系统结构-第十章 输入输出(I/O)系统

    输入输出(I/O)系统 一.概述 1.输入输出设备的编址 2.设备控制器(I/O接口)的基本功能 3.I/O设备数据传送控制方式 二.程序中断输入输出方式 1.中断的定义 2.中断的作用 3.中断的产 ...

  5. Algorithms - Priority Queue - 优先队列

    Priority queue - 优先队列 相关概念 Priority queue优先队列是一种用来维护由一组元素构成的集合S的数据结构, 其中的每一种元素都有一个相关的值,称为关键字(key). 一 ...

  6. python语法学习第二天--条件与循环

    条件:(一条或多条为true或false的语句.任何非0和非空(null)值为true,0 或者 null为false)若为true执行If下的语句,若为false执行else中的语句 分支: ① i ...

  7. [CodeForces 344C Rational Resistance]YY,证明

    题意:给若干个阻值为1的电阻,要得到阻值为a/b的电阻最少需要多少个. 思路:令a=mb+n,则a/b=m+n/b=m+1/(b/n),令f(a,b)表示得到a/b的电阻的答案,由f(a,b)=f(b ...

  8. spring boot构建restful服务

    使用spring boot快速构建出restful服务 JPA实现REST 创建spring boot项目,在项目文件pom.xml中添加以下依赖: <dependency> <gr ...

  9. windows电脑关闭自动更新的方法

    第一步.打开我的电脑,点击此电脑,然后点击管理 第二步.在计算机管理(本地)里面找到服务和应用程序,点击进入 第三步.进去,点击服务 第四步.往下滑,找到windows update,点击进入 第五步 ...

  10. Flutter不能做什么:局限性

    老孟导读:您在网络上一定看过很多Flutter如何优秀的.如何完美的文章,而这篇文章将会告诉你Flutter不能做什么,注意并不是Flutter的缺点,比如第三方插件少.Dart不流行等,在我看来这都 ...