Java多线程笔记总结
1.线程的三种创建方式
对比三种方式:
- 通过继承Thread类实现
- 通过实现Runnable接口
- 实现Callable接口
第1种方式无法继承其他类,第2,3种可以继承其他类;
第2,3种方式多线程可以共享同一个target对象,多个线程处理同一个资源;
一般使用第2,3种方式创建线程。
2.线程的生命周期
1.新建(new) 2.就绪(start) 3.运行(获得cpu资源) 4.阻塞(sleep,IO阻塞等)4.死亡(执行完成,Exception/Error)
3.线程常用方法
join() :Thread对象调用,线程A调用join(),其他线程被阻塞,直到线程A执行完为止。
setDaemon(true) : Thread对象调用,设置成后台线程; 当所有前台线程都死亡,后台线程自动死亡。
- sleep(): 静态方法,让正在执行的线程暂停,进入阻塞状态。
- yield(): 静态方法,让正在进行的线程暂停,进入就绪状态,系统的线程调度器重新调度一次;只有优先级比当前调用yield()的线程高或相同,并且处于就绪状态的线程才能获得执行。
- setPriority(int newPriority), getPriority(): 设置和获取线程的优先级;newPriority范围1-10;Thread三个静态常量,MAX_PRIORITY, 10; NORM_PRIORITY, 5; MIN_PRIORITY, 1;子线程和父线程具有相同的优先级,优先级高的线程比优先级低的线程执行机会更多。
4.线程同步
多个线程访问同一个数据时(线程调度的不确定性),很容易出现线程安全问题。解决办法:引入同步监视器,任意时刻只能有一个线程获得对同步监视器的锁定,当同步代码块执行结束后,该线程释放对该同步监视器的锁定。
“加锁”—>”修改共享资源”->”释放锁”
同步代码块
同步方法
使用synchronized修饰某个方法,则该方法称为同步方法,同步方法的同步监视器是this;
只需对会改变竞争资源的方法进行同步。
任何线程在进入同步代码块或同步方法前,必须获得同步监视器的锁定。
释放同步监视器的锁定的时机:
- 当前线程的同步代码块,同步方法执行结束
- 当前线程在同步代码块,同步方法中遇到break,return终止了执行
- 当前线程在同步代码块,同步方法中出现了未处理的Error或Exception
- 执行了同步监视器对象的wait()方法
同步锁(Lock)
Lock对共享资源的独占访问,每次只能一个线程对Lock对象加锁。
Lock,ReadWriteLock接口,对应的实现类ReentrantLock(可重入锁),ReentrantReadWriteLock。
死锁
两个线程互相等待对方释放同步监视器,就发生了死锁。
线程池
在系统启动时,创建大量空闲的线程,将一个Runnable对象或Callable对象传给线程池,线程池会启动一个线程执行对应的run()或call(),当run()或call()执行完成后,该线程返回到线程池成为空闲状态,等待下个Runnable对象或Callable对象。
创建线程池
Executors工厂类,提供如下静态方法创建不同的线程池:
步骤:
- Executors静态工厂类创建ExecutorService
- 创建Runnable或Callable对象,作为线程执行体
- 调用ExecutorService实例的submit()方法提交Runnable或Callable
- 线程池关闭,调用ExecutorService实例的shutdown()方法
ThreadLocal
线程局部变量,把数据放在ThreadLocal中,就可以为每个线程创建一个该变量的副本,避免并发访问的线程安全问题。
与同步机制的区别:
同步机制是为了同步多个线程对共享资源的并发访问,是多线程间通信的有限方式;
ThreadLocal隔离多个线程的数据共享,避免多个线程间对共享资源的竞争,不需要对多个线程进行同步。
线程安全集合
通过Collections包装成线程安全集合:
ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap等都是线程不安全;如果多个线程对这些集合读,写时,会破坏这些集合数据的完整性。
Collections提供静态方法将这些集合包装成线程安全的集合。
----------------------------------------------------------------------------------------
线程安全集合:
ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, ConcurrentLinkedQueue, ConcurrentLinkedDeque
CopyOnWriteArrayList
CopyOnWriteArraySet
Java线程初体验
Java.lang
class Thread
interface Runnable
public void run()
线程的常用方法
类别 |
方法签名 |
简介 |
线程的创建 |
Thread() |
|
Thread(String name) |
||
Thread(Runnable target) |
||
Thread(Runnable target,String name) |
||
线程的方法 |
void start() |
启动线程 |
static void sleep(long millis) |
线程休眠 |
|
static void sleep(long millis,int nanos) |
||
void join() |
使其他线程等待当前线程终止 |
|
void join(long millis) |
||
void join(long millis,int nanos) |
||
static void yield() |
当前运行线程释放处理器资源 |
|
获取线程引用 |
static Thread currentThread() |
返回当前运行的线程引用 |
两条线程不做任何处理的时候,会交替运行。
当使用boolean类型控制线程的循环时,要在变量前加上volatile关键字,volatile保证了线程可以正确的读取其他线程写入的值。
注意点:
sleep()方法的作用:使线程休眠指定的时间。
join()方法的作用: //使其他线程等待当前线程执行完毕。
线程的正确停止
如何正确的停止Java中的线程:
可以通过boolean类型来控制循环的退出。
not stop方法
stop()方法会让线程戛然而止。
stop()方法停止线程是错误的方法。
线程的交互
争用条件Race Condition
当多个线程同时共享访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏(corrupted),这种现象称为争用条件。
什么是互斥?
互斥是怎么实现的?
只能被一个线程所访问,互斥。
互斥的实现:synchronized(intrinsic lock)加锁。
同步的实现:wait()/notify()/notifyAll()。
如何扩展Java并发的知识
Java Memory Mode
JMM描述了Java线程如何通过内存进行交互
Happens-before
Synchronized,volatile&final
Locks&Condition
Java锁机制和等待条件的高层实现
Java.util.concurrent.locks
线程安全性
原子性与可见性
Java.util.concurrent.atomic
Synchronized&volatile
DeadLocks
多线程编程常用的交互模型
Producer-Consumer模型
Read-Write Lock模型
Future模型
Worker Thread模型
Java5中并发编程工具
Java.util.concurrent
线程池ExecutorService
Callable&Future
BlockingQueue
推荐两本书:
Core Java
Java concurrency in practice
线程创建的两种方式比较
Thread:
① 继承Thread类;
Runnable:
① Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷。
② Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况。
使用jstack生成线程快照
jstack
作用:生成jvm当前时刻线程的快照(threaddump,即当前进程中所有线程的信息)
目的:帮助定位程序问题出现的原因,如长时间停顿、cpu占用率过高等。
如何使用jstack
jstack –l pid
内存可见性
可见性介绍
可见性:一个线程对共享变量值的修改,能够及时地被其他线程看到。
共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量。
Java内存模型(JMM)
Java内存模型(Java Memory Model)描述了程序中各种变量(线程共享变量)的访问规则,以及在Java中将变量存储到内存和从内存中读取出变量这样的底层细节。
所有变量都存储在主内存中
每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)。
线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写。
不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
共享变量可见性实现的原理
线程1对共享变量的修改要想被线程2及时看到,必须要经过如下2个步骤:
把工作内存1中更新过的共享变量刷新到主内存中。
将主内存中最新的共享变量的值更新到工作内存2中。
可见性
要实现共享变量的可见性,必须保证两点:
线程修改后的共享变量值能够及时从工作内存刷新到主内存中。
其他线程能够及时把共享变量的最新值从主内存更新到自己的工作内存中。
可见性的实现方式
Java语言层面支持的可见性实现方式:
Synchronized
Volatile
Synchronized实现可见性
Synchronized能够实现:
原子性(同步);
可见性
JMM关于Synchronized的两条规定:
线程解锁前,必须把共享变量的最新值刷新到主内存中;
线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁与解锁需要是同一把锁)
线程解锁前对共享变量得修改在下次加锁时对其他线程可见。
线程执行互斥代码的过程:
- 获得互斥锁
- 清空工作内存
- 从主内存中拷贝变量的最新副本到工作内存
- 执行代码。
- 将更改后的共享变量的值刷新到主内存。
- 释放互斥锁。
重排序
重排序:代码书写的顺序与实际执行的顺序不同,指令重排序是编译器或处理器为了提高程序性能而做的优化。
- 编译器优化的重排序(编译器优化)。
- 指令级并行重排序(处理器优化)。
- 内存系统的重排序(处理器优化)。
as-if-serial
as-if-serial:无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致(java编译器、运行时和处理器都会保证Java在单线程下遵循as-if-serial语义)。
例子:
Int num = 1;
Int num2 = 2;
Int sum = num+num2;
单线程:第1、2行的顺序可以重排,但第3行不能
重排序不会给单线程带来内存可见性问题
多线程中程序交错执行时,重排序可能会造成内存可见性问题。
Volatile实现可见性
Volatile关键字:
能够保证volatile变量的可见性
不能保证volatile变量复合操作的原子性
Volatile如何实现内存可见性:
深入来说:通过加入内存屏障和禁止重排序优化来实现的。
对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
对volatile变量执行读操作时,会在读操作前加入一条load屏障指令
通俗的讲:volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同线程总能看到该变量的最新值。
线程写volatile变量的过程:
- 改变线程工作内存中volatile变量副本的值
- 将改变后的副本的值从工作内存刷新到主内存
线程读volatile变量的过程:
- 从主内存中读取volatile变量的最新值到线程的工作内存中
- 从工作内存中读取volatile变量的副本。
Volatile适用场合
要在多线程中安全的使用volatile变量,必须同时满足:
1. 对变量的写入操作不依赖其当前值
2. 布尔,用来记录温度
3.该变量没有包含在具有其他变量的不变式中。
Java多线程笔记总结的更多相关文章
- Java多线程笔记[未更新完]
最近课上可摸鱼时间较多,因此并发开坑学习 本篇学习自Java多线程编程实战指南 目前进展:刚开坑,处于理解概念阶段 本篇学习自Java多线程编程实战指南 Q.进程和线程的区别 进程Process是程序 ...
- Java 多线程 笔记 转自http://www.cnblogs.com/lwbqqyumidi/p/3804883.html
多线程作为Java中很重要的一个知识点, 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中 ...
- 这份java多线程笔记,你真得好好看看,我还没见过总结的这么全面的
1.线程,进程和多线程 1.程序:指指令和数据的有序集合,其本身没有任何意义,是一个静态的概念 2.进程:指执行程序的一次执行过程,是一个动态的概念.是系统资源分配的单位(注意:很多多线程是模拟出来的 ...
- Java 多线程笔记
资料来源于网络,仅供参考学习. 1.A Java program ends when all its threads finish (more specifically, when all its ...
- java多线程笔记
一,线程的状态 1,新建状态:新创建了一个线程对象 2,就绪状态:线程创建对象后,线程调用star()的方法,等待获取CPU的使用权. 3,运行状态:获取了cpu的使用权,执行程序代码 4,阻塞状态: ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- Java多线程技术学习笔记(二)
目录: 线程间的通信示例 等待唤醒机制 等待唤醒机制的优化 线程间通信经典问题:多生产者多消费者问题 多生产多消费问题的解决 JDK1.5之后的新加锁方式 多生产多消费问题的新解决办法 sleep和w ...
- java多线程学习笔记——详细
一.线程类 1.新建状态(New):新创建了一个线程对象. 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...
随机推荐
- 【转】 远程连接mysql
转自:http://www.linuxidc.com/Linux/2013-05/84813.htm 1.确认能ping通 2.确认端口能telnet通.如果user表的host值是localhost ...
- PATB 1015. 德才论 (25)
1015. 德才论 (25) 比较函数折腾好久,最后还因为cout,printf的区别而超时,超时是因为cout输出效率低. 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 ...
- 可用的NTP服务器地址
国内可用的Internet时间同步服务器地址(NTP时间服务器)好在阿里云提供了7个NTP时间服务器也就是Internet时间同步服务器地址 ntp1.aliyun.comntp2.aliyun.co ...
- 总结关于CPU的一些基本知识
关于CPU和程序的执行 CPU是计算机的大脑. 程序的运行过程,实际上是程序涉及到的.未涉及到的一大堆的指令的执行过程. 当程序要执行的部分被装载到内存后,CPU要从内存中取出指令,然后指令解码(以便 ...
- WIFI密码破解全攻略
开篇介绍 目前无线网络加密技术日益成熟.以前的wep加密方式日渐淘汰,因为这种加密方式非常容易破解,当然现在还是有不少使用这种加密方式无线网络.现在大部分的无线网络都是使用wpa/wpa2方式来加密的 ...
- mac 添加new file.md
1. 打开mac自带的"Automator",文稿类型选择应用程序:   2. 选择:实用工具 -> 运行 AppleScript  3. 黏贴如下代码到上图的右侧,c ...
- 仿写一个简陋的 IOC/AOP 框架 mini-spring
讲道理,感觉自己有点菜.Spring 源码看不懂,不想强行解释,等多积累些项目经验之后再看吧,但是 Spring 中的控制反转(IOC)和面向切面编程(AOP)思想很重要,为了更好的使用 Spring ...
- 微服务-springcloud-注册中心
创建服务注册中心(eureka-server) 1.创建项目,选择 Eureka Server 别的都不要选择,next-finish 2.application.yml中写入如下信息:通过eurek ...
- django的阶段总结
Django回顾 1 web应用 本质是基于socket实现的应用程序 浏览器-----------服务器 2 http协议:应用层协议 1 基于TCP协议 2 基于请求响应 3 短连接 4 无状态保 ...
- scrapy实战5 POST方法抓取ajax动态页面(以慕课网APP为例子):
在手机端打开慕课网,fiddler查看如图注意圈起来的位置 经过分析只有画线的page在变化 上代码: items.py import scrapy class ImoocItem(scrapy.It ...