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()方法.该状态的线程位于可运行线程池中, ...
随机推荐
- 利用Shell开发MySQL的启动脚本
MySQL实例部署情况 01:MySQL程序安装目录:/data/apps/mysql 02:MySQL实例3306的配置文件为:/data/mysql/3306/my.cnf 03:MySQL实例3 ...
- Binary classification - 聊聊评价指标的那些事儿【实战篇】
分类问题就像披着羊皮的狼,看起来天真无害用起来天雷滚滚.比如在建模前你思考过下面的问题么? 你的分类模型输出的概率只是用来做样本间的相对排序,还是概率本身? 你的训练数据本身分布如何是否存在Imbal ...
- 线性模型之LDA和PCA推导
线性模型之LDA和PCA 线性判别分析LDA LDA是一种无监督学习的降维技术. 思想:投影后类内方差最小,类间方差最大,即期望同类实例投影后的协方差尽可能小,异类实例的投影后的类中心距离尽量大. 二 ...
- 前端Web浏览器基于Flash如何实时播放监控视频画面(前言)之流程介绍
[关键字:前端浏览器如何播放RTSP流画面.前端浏览器如何播放RTMP流画面] 本片文章只是起到抛砖引玉的作用,能从头到尾走通就行,并不做深入研究.为了让文章通俗易懂,尽量使用白话描述. 考虑到视频延 ...
- 分布式个人理解概述和dubbo实现简述
什么是分布式?为什么使用分布式? 个人有一些浅薄的理解希望可以批评指正,从概念和应用 两个方面概述: 一.概念:分布式也叫分布式服务,也就是说 他是 一种面向服务思想的程序设计和架构风格,典 ...
- js常用设计模式实现(一)单例模式
前言 什么是设计模式 设计模式是一种能够被反复使用,符合面向对象特性的代码设计经验的总结,合理的使用设计模式能够让你得代码更容易维护和可靠 设计模式的类型共分为创建型模式,结构型模式,行为型模式三种 ...
- 深入学习Spring框架(二)- 注解配置
1.为什么要学习Spring的注解配置? 基于注解配置的方式也已经逐渐代替xml.所以我们必须要掌握使用注解的方式配置Spring. 关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯 ...
- C# 创建Windows服务demo
一.准备工作 1.操作系统:Windows 10 X64 2.开发环境:VS2017 3.编程语言:C# 4. .NET版本:.NET Framework 4.5 二.创建Windows Servic ...
- AD域和LDAP协议
随着我们的习大大上台后,国家在网络信息安全方面就有了很明显的改变!所以现在好多做网络信息安全产品的公司和需要网络信息安全的公司都会提到用AD域服务器来验证,这里就简单的研究了一下! 先简单的讲讲AD域 ...
- 0x31 prime distance(质数)
题目描述: 给定两个整数L和U,你需要在闭区间[L,U]内找到距离最接近的两个相邻质数C1和C2(即C2-C1是最小的),如果存在相同距离的其他相邻质数对,则输出第一对. 同时,你还需要找到距离最远的 ...