美文网首页java
java并发学习

java并发学习

作者: random123 | 来源:发表于2020-03-27 00:01 被阅读0次

参考 我没有三颗心脏

https://www.jianshu.com/p/7382c0a843ff
https://www.jianshu.com/p/cd9d0927be35

线程与进程

线程是操作系统中分配和管理资源的基本单位

进程是执行运算的最小单位,线程是进程的一个实体

线程没有地址空间,线程包含在进程的地址空间中。线程上下文只包含一个堆栈、一个寄存器、一个优先权,线程文本包含在他的进程的文本片段中,进程拥有的所有资源都属于线程。所有的线程共享进程的内存和资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段 寄存器的内容,栈段又叫运行时段,用来存放所有局部变量和临时变量。(线程共享代码段、数据段和堆空间,拥有独立的栈空间和寄存器内容)

子进程不对任何其他子进程施加控制,进程的线程可以对同一进程的其它线程施加控制。子进程不能对父进程施加控制,进程中所有线程都可以对主线程施加控制。

进程切换时,涉及到当前进程的 CPU 环境的保存和新被调度运行进程的 CPU 环境的设置。线程切换仅需要保存和设置少量的寄存器内容,不涉及存储管理方面的操作。

java多线程实现方式

extends Thread

继承Thread类


implements Runnable

实现Runnable接口

implement Callable

注意run() 方法和 start() 区别

start() 方法使创建好的线程从runnable状态到running状态;而直接调用 run() 方法知识作为一个普通的方法调用而已,它只会在当前线程中,串行执行 run() 中的代码

synchronized和ReentrantLock(Lock类)

参考:https://www.cnblogs.com/takumicx/p/9338983.html

.ReentrantLock和synchronized都是独占锁,只允许线程互斥的访问临界区。

synchronized加锁解锁的过程是隐式的,用户不用手动操作,优点是操作简单,但显得不够灵活。ReentrantLock需要手动加锁和解锁,且解锁的操作尽量要放在finally代码块中,保证线程正确释放锁。

synchronized因为可重入因此可以放在被递归执行的方法上,且不用担心线程最后能否正确释放锁;而ReentrantLock在重入时要却确保重复获取锁的次数必须和重复释放锁的次数一样,否则可能导致其他线程无法获得该锁。

在创建ReentrantLock的时候通过传进参数true创建公平锁,如果传入的是false或没传参数则创建的是非公平锁;而synchronized是非公平锁

当使用synchronized实现锁时,阻塞在锁上的线程除非获得锁否则将一直等待下去,也就是说这种无限等待获取锁的行为无法被中断。而ReentrantLock给我们提供了一个可以响应中断的获取锁的方法lockInterruptibly()

使用synchronized结合Object上的wait和notify方法可以实现线程间的等待通知机制。ReentrantLock结合Condition接口的await()和signal()方法同样可以实现这个功能。而且相比前者使用起来更清晰也更简单。

“阻塞”与"非阻塞"   "同步"与“异步"

同步和异步关注的是消息通信机制

同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。


线程5大状态

new/Runnable/Running/Blocked/Dead

状态及转移关系

注:线程阻塞情况有多种

(一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。

(二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。

(三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态

并发(Concurrency)和并行(Parallelism)的区别

并行性是指两个或多个事件在同一时刻发生。而并发性是指连个或多个事件在同一时间间隔内发生。

Monitor机制

当一个线程需要访问受保护的数据(即需要获取对象的Monitor)时,它会首先在entry-set入口队列中排队(这里并不是真正的按照排队顺序),如果没有其他线程正在持有对象的Monitor,那么它会和entry-set队列和wait-set队列中的被唤醒的其他线程进行竞争(即通过CPU调度),选出一个线程来获取对象的Monitor,执行受保护的代码段,执行完毕后释放Monitor,如果已经有线程持有对象的Monitor,那么需要等待其释放Monitor后再进行竞争。

再说一下wait-set队列。当一个线程拥有Monitor后,经过某些条件的判断(比如用户取钱发现账户没钱),这个时候需要调用Object的wait方法,线程就释放了Monitor,进入wait-set队列,等待Object的notify方法(比如用户向账户里面存钱)。当该对象调用了notify方法或者notifyAll方法后,wait-set中的线程就会被唤醒,然后在wait-set队列中被唤醒的线程和entry-set队列中的线程一起通过CPU调度来竞争对象的Monitor,最终只有一个线程能获取对象的Monitor。

通过上图可以得知,调用obj.wait()时,当前线程必须获取到了obj的Monitor。即wait必须放在同步方法或同步代码块中。

调用wait方法,就意味着释放了Monitor;调用notify方法,并不意味着释放了Monitor,必须要等同步代码块结束后才会释放Monitor。

Volatile关键字

保证变量的可见性:将修改的值立即写入主存,对值的修改是其他线程工作内存的stop缓存失效,使得其他线程去主存读取更新自己的缓存。

禁止指令重排:插入内存屏障(内存屏障????


线程池

包括工作线程和等待队列

如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队;如果运行的线程等于或者多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不是添加新线程;如果等待队列已满,但是当前线程大小小于maxinumPoolSize,创建新的线程;如果核心线程数、等待队列、最大线程数都满,则使用handler处理任务。

handler策略:1.AbortPolicy 抛出异常  2.DiscardPolicy直接丢弃    3.DiscardOldestPolicy 丢弃队列中最旧的    4.CallerRunsPolicy 不用线程池中的线程,用调用者所在线程执行。

线程池的好处:1.减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务    2.提高响应速度,不需要等待线程创立即可执行    3.提高线程的可管理性    4.避免创建大量线程,导致消耗系统资源。

相关文章

网友评论

    本文标题:java并发学习

    本文链接:https://www.haomeiwen.com/subject/znjhuhtx.html