Java并发性和多线程

时间:2019-03-18
本文章向大家介绍Java并发性和多线程,主要包括Java并发性和多线程使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

转载自http://ifeve.com/java-concurrency-thread-directory/

简要总结

并发性和多线程的区别和联系

多线程是并发的一种实现。并发还可以使用分布式/多进程实现。

并发模型:并行工作者、流水线工作者/事件驱动模型

Java多线程两/四种创建方式

继承Thread,(匿名/不匿名)子类

实现Runnable接口,(匿名/不匿名)类

竞态条件(race condition)和临界区()

当多个线程同时访问某一代码区,对共享的资源做出了更改,若运行结果与多个线程的访问顺序有关,则称形成了竞态条件,与之相关的这一代码区域称为临界区。

多个线程同时更新共享资源时会形成竞态条件。

线程安全与共享资源

多线程中某些资源是被共享的。多个线程同时更新共享资源时会形成竞态条件。允许被多个线程同时执行的代码称作线程安全的代码。线程安全的代码不包含竞态条件。要明白哪些资源是线程安全的(方便使用的)。

  • 局部基本变量(线程安全)
  • 局部对象引用(方法中对象的引用没有逃逸,或者没有被其他线程引用,就是安全的)
  • 对象成员(不安全)
  • 使用不可变对象(没有setter的类实例,线程安全)
  • ThreadLocal(创建的变量只被同一个线程读写,在多个线程中有不同的副本,仅初始值相同。线程安全)

参考java内存模型可以明白它们为什么是线程安全或不安全的。

线程控制逃逸规则:如果一个资源的打开、使用、销毁都是在一个线程中完成的。且不会脱离该线程的控制,该资源的使用就是线程安全的。

资源:包括对象、数组、文件、数据库连接、套接字等。

即使对象本身是线程安全的,但对象使用的资源可能不安全,如包含了数据库多步操作。

Java同步块

多线程开发时,要使用线程安全的资源(不存在竞争)。对于线程不安全的资源/存在竞争的的情况,使用同步块来避免竞争。

同步块可以保证一次只有一个线程进入临界区(加同步块的代码区),其他线程被阻塞(不恰当的编程可能导致一直阻塞,也就是死锁)。Java中的同步块使用synchronized关键字来标记。同步都加在类对象上。

实例方法同步

静态方法同步

实例方法中的同步块

静态方法中的同步块

线程通信

在保证线程安全的基础上,线程间应当可以通信

使用共享对象通信:共享对象中加锁(加同步块)防止竞争,在不同线程中使用此对象。

简单实现:共享对象中加锁(加同步块)防止竞争,线程1发送消息,线程2通过轮询等待(忙等待,缺点是cpu资源消耗大)

Java内建通信机制:共享对象加锁(使用同步块),在同步块中使用wait()\notify()\notifyAll()函数。

解决信号丢失(notify在wait之前):Java内建通信机制+判断共享对象成员变量(保存notify状态)

解决假唤醒(特殊情况下wait返回向后执行):Java内建通信机制+循环判断共享对象成员变量(保存notify状态)

注意不要使用字符串常量作为共享对象(JVM中相同的字符串常量指向了同一个地址),还有全局对象。

死锁

同步块解决线程不安全代码的竞争问题,但同步块可能有死锁的隐患。当一个线程进入同步块(代码临界区),其他线程被阻塞(不恰当的编程可能导致一直阻塞,也就是死锁)。

例子:如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。

2+线程的死锁

数据库更新记录的死锁

死锁避免

加锁顺序

加锁时限

死锁检测


mark vert.x

未学习:java中的锁,信号量,线程池等