Java多线中基础知识整理
Java多线程中相关术语:
一、Java多线程原则
1.原子性:一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。一般使用线程同步或者Lock锁来确保。
2.可见性(Java内存模型):当多个线程同时访问一个变量时,其中某个线程改变改变量值,其他线程需要立即查询到。
3.有序性(涉及JVM对代码的重排序问题):编译器和处理器对不影响逻辑的代码进行重排序(执行顺序),以便提高运行速率(涉及Java的内存屏障,可以使用volatile来禁止重排序)。
备注:指令重排序时不能把后面的指令重排序到内存屏障之前的位置。
二、Java内存模型(JMM)
1.JMM决定了一个线程对共享变量进行操作后,能否对其他线程可见。
2.JMM为每个线程抽象出一个本地内存概念,让一个线程对共享变量进行修改时,先修改本地内存中的值,然后在将本地内存中的值刷新到主线程中供其他线程读取。
其中volatile关键字修饰的变量,会被立即更新到主内存中,供其他线程所调用。
备注(volatite的作用域):在Java中为了加快程序的运行效率,对变量的操作是在寄存器或者CPU上,之后在同步到主内存中,使用volatile修饰后会立即同步至主内存中。
Java 中的两种线程:
- 守护线程 : 主线程停止后,守护线程会立即停止( 例如:GC 线程) ,Java 中使用setDaemon(true) 方法来设置为守护线程。
- 用户线程 : 用户自定义的线程,当主线程停止后,用户不会立即停止。
Java 中多线程的几个相关状态:
新建状态、就绪状态 ( 调用start 方法后) 、运行状态、阻塞状态及死亡状态。
Java 多线程中常用的方法:
join(): 将执行权让给被调用方。
setPriority(): 设置获取CPU 调度优先级。
yield():放弃当前CUP 执行权, 和其他线程在一次竞争CPU 使用权。
wait():停止当前执行的线程,释放锁让其他线程获取该锁。
notify():唤醒因锁而阻塞的线程。
注意:wait和notify要与synchronized一起使用,sleep方法不会释放锁。
使用wait和notify实现消费者和生产者(同步方式)[代码示例:wait和notify实现]
Java多线程中锁的分类:
重入锁:同一个线程中,函数之间调用或者递归调用,锁是可传递的,也称递归锁。特点:1.当程序进入同步代码快时自动获取锁,程序执行完毕后或者有异常抛出时自动释放锁。2.该锁是互斥锁。3.使用synchronized修饰[代码示例1-1]。
synchronized是重量级锁,Lock中ReentrantLock是轻量级锁,使用时需要手动释放锁。
注意:1.同步方法使用的是this锁。2.静态同步函数采用的锁是字节码文件对象(类.class)。
读写锁:允许多个线程同时读同一个共享资源时,一旦有写操作时将禁止其他线程读取(读-读可共存,读-写不共存,写-写不共存),适用于对共享资源写操作少。
ReentrantReadWriteLock[代码示例:读写锁实现]
乐观锁:思想是默认认为不会发生并发问题,每次取数据时认为没有其他线程对数据进行修改过,但在更新数据时使用版本号或者CAS方式判断在自己保存之前是否有数据被更改过,如果没有则保存,若果已经被修改则重试。
版本号(version)方式:在获取数据的表中添加一个version字段,标注当前数据被更新的次数,在更新数据之前先查询version值,在保存时再次查询version值是否被更改,一直重试到当前version和数据库一致才能保存。
eg: update table set x = x +1, version = version + 1 where x = #{x} and version = #{version};
CAS(compare and swap)方式:有3个关键词,主内存值(V):,预期值:本地内存(E),新值:更新后的值(N)
当V=E(主内存值等于本地内存值),表示数据没有被修改过,此时可以进行变更。
当V!=E,表示数据被其他线程修改过,此时重新将主内存中的值刷新到本地内存中,然后再重新比较。
jdk底层源码案例[CAS源码]
CAS缺点:无法解决ABA问题,即一个线程先将值由A改成了B,然后里一个线程在由B改成了A,此时C线程会当做A没有被修改过。可以使用AtomicStampedReference类解决(原理是用时间戳记录).
jdk提供常用的实现了CAS的类:AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference等
悲观锁:在操作数据时,认为每次都会有其他线程去修改,所以读取都会被加锁。
分布式锁:缓存实现(Redis)、Zookeeper实现等。
Java多线程解决方案:
ThreadLocal:为每个线程提供一个自己的局部变量(隔离共享变量)。
ThreadLocal方法简介:
void set(Object value) 设置当前线程的线程局部变量的值。
Object get() 返回当前线程所对应的线程局部变量。
void remove()将当前线程局部变量的值删除(线程执行完毕后,jdk将自动回收ThreadLoacl的局部变量)[代码示例2-1]。
ThreadLocal实现原理:底层采用Map进行封装,key为当前线程,value为需要保存的值。
=================================================================================
多线程使用不当导致的问题集锦:
1.wait和notify实现。
class Resource { public String name;
public Integer sex;
public boolean isCustomer = false;
}
// 生产者
class Produces extends Thread { private Resource resource; Produces(Resource resource) {
this.resource = resource;
} @Override
public void run() {
int count = 0;
while(true) {
synchronized (resource) {
if(resource.isCustomer) {
try {
resource.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(count == 0) {
resource.name = "produce";
resource.sex = 0;
} else {
resource.name = "cusoumer";
resource.sex = 1;
}
count = (count + 1) % 2;
resource.isCustomer = true;
resource.notify();
}
}
}
}
// 消费者
class Cusoumers extends Thread { private Resource resource; Cusoumers(Resource resource) {
this.resource = resource;
} @Override
public void run() {
while(true) {
synchronized (resource) {
try {
if(!resource.isCustomer) {
resource.wait();
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前消费:" + resource.name + " " + resource.sex);
resource.isCustomer = false;
resource.notify();
}
}
}
} public class WaitAndNotify { public static void main(String[] args) { Resource resource = new Resource(); Produces produce = new Produces(resource);
Cusoumers cusoumer = new Cusoumers(resource);
produce.start();
cusoumer.start(); } }
2.当在使用多线程时,线程间同时对某个共享的全局变量或者静态变量进行写操作时,将会发生数据冲突问题。
eg(1-1):
@SuppressWarnings("static-access")
public class MultiThread implements Runnable { private Integer count = 100; @Override
public void run() { while(count > 0) {
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
sale();
}
} private void sale() {
if(count > 0) {
System.out.println("第" + (100 - count + 1) + "张票售出");
count--;
}
} public static void main(String[] args) { MultiThread multiThread = new MultiThread(); Thread m1 = new Thread(multiThread, "1号窗口");
Thread m2 = new Thread(multiThread, "2号窗口"); m1.start();
m2.start();
} }
使用内置锁来处理(不推荐,性能比较低)
private void sale() {
synchronized (this) {
if(count > 0) {
System.out.println("第" + (100 - count + 1) + "张票售出");
count--;
}
}
}
2.ThreadLocal
eg(2-1):
class Produce{ private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() { return 0;
}
}; public Integer getCount() { Integer nextCount = threadLocal.get() + 1;
threadLocal.set(nextCount); return nextCount;
} } public class ThreadLocalTest extends Thread { private Produce produce; ThreadLocalTest(Produce produce) {
this.produce = produce;
} @Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + "___" + produce.getCount());
}
} public static void main(String[] args) { Produce produce = new Produce();
ThreadLocalTest t1 = new ThreadLocalTest(produce);
ThreadLocalTest t2 = new ThreadLocalTest(produce);
ThreadLocalTest t3 = new ThreadLocalTest(produce);
t1.start();
t2.start();
t3.start(); } }
编译器对代码进行重排序
class Reorder { int a = 0;
boolean flag = false; public void writer() {
a = 1; // 当线程执行是a=1和flag=true时,编译器可能会先执行flag=true,然后在执行a=1
flag = true;
} public void reader() {
int i = 0;
if(flag) {// 当线程执行是a=1和flag=true时,编译器可能会先执行i=a*a,然后在执行if(flag)
i = a * a;
System.out.println(i);
}
}
} class ReorderThread1 extends Thread { private Reorder reorder; ReorderThread1(Reorder reorder) {
this.reorder = reorder;
} @Override
public void run() {
reorder.writer();
} } public class ReorderThread extends Thread { private Reorder reorder; ReorderThread(Reorder reorder) {
this.reorder = reorder;
} @Override
public void run() {
reorder.reader();
} public static void main(String[] args) { Reorder reorder = new Reorder();
ReorderThread t1 = new ReorderThread(reorder);
ReorderThread1 t2 = new ReorderThread1(reorder); t1.start();
t2.start(); }
}
3.读写锁
public class ReadWriteLockTest { public static Map<Integer, Integer> maps = new HashMap<>();
public static ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public static Lock readLock = readWriteLock.readLock();
public static Lock writeLick = readWriteLock.writeLock(); public static Integer getResult(Integer key) { readLock.lock();
Integer result = null;
try {
System.out.println("=======读=======");
Thread.currentThread().sleep(50);
result = maps.get(key);
System.out.println(key);
System.out.println("=======读结束=======");
} catch (Exception e) {
e.printStackTrace();
} finally {
readLock.unlock();
} return result;
} public static void putResult(Integer key, Integer value) { writeLick.lock();
try {
System.out.println("=======写=======");
Thread.currentThread().sleep(50);
maps.put(key, value);
System.out.println(key + "_" + value);
System.out.println("=======写结束=======");
} catch (Exception e) {
e.printStackTrace();
} finally {
writeLick.unlock();
} } public static void main(String[] args) { new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 100; i++) {
putResult(i, i);
}
}
}).start(); new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 100; i++) {
getResult(i);
}
}
}).start();
}
}
若屏蔽掉读写锁,如下
3. CAS源码
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
} /**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
//获取当前值
int current = get();
//设置期望值
int next = current + 1;
//调用Native方法compareAndSet,执行CAS操作
if (compareAndSet(current, next))
//成功后才会返回期望值,否则无线循环
return next;
}
}
Java多线中基础知识整理的更多相关文章
- java部分基础知识整理----百度脑图版
近期发现,通过百度脑图可以很好的归纳总结和整理知识点,本着学习和复习的目的,梳理了一下java部分的知识点,不定期更新,若有不恰之处,请指正,谢谢! 脑图链接如下:java部分基础知识整理----百度 ...
- Kali Linux渗透基础知识整理(二)漏洞扫描
Kali Linux渗透基础知识整理系列文章回顾 漏洞扫描 网络流量 Nmap Hping3 Nessus whatweb DirBuster joomscan WPScan 网络流量 网络流量就是网 ...
- 《Java核心技术·卷Ⅰ:基础知识(原版10》学习笔记 第5章 继承
<Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 目录 <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 5.1 类.超类和子类 5.1 ...
- Kali Linux渗透基础知识整理(四):维持访问
Kali Linux渗透基础知识整理系列文章回顾 维持访问 在获得了目标系统的访问权之后,攻击者需要进一步维持这一访问权限.使用木马程序.后门程序和rootkit来达到这一目的.维持访问是一种艺术形式 ...
- 【OGG】OGG基础知识整理
[OGG]OGG基础知识整理 一.GoldenGate介绍 GoldenGate软件是一种基于日志的结构化数据复制软件.GoldenGate 能够实现大量交易数据的实时捕捉.变换和投递,实现源数据库与 ...
- wifi基础知识整理
转自 :http://blog.chinaunix.net/uid-9525959-id-3326047.html WIFI基本知识整理 这里对wifi的802.11协议中比较常见的知识做一个基本的总 ...
- JavaScript基础知识整理
只整理基础知识中关键技术,旨在系统性的学习和备忘. 1.在 JScript 中 null 和 undefined 的主要区别是 null 的操作象数字 0,而 undefined 的操作象特殊值NaN ...
- 如何看K线图基础知识
在日K线图中一般白线.黄线.紫线.绿线依次分别表示:5.10.20.60日移动平均线,但这并不是固定的,会根据设置的不同而不同,比如你也可以在系统里把它们设为5.15.30.60均线. 你看K线图的上 ...
- Java学习之旅基础知识篇:数据类型及流程控制
经过开篇对Java运行机制及相关环境搭建,本篇主要讨论Java程序开发的基础知识点,我简单的梳理一下.在讲解数据类型之前,我顺便提及一下Java注释:单行注释.多行注释以及文档注释,这里重点强调文档注 ...
随机推荐
- 【原创】运维基础之keepalived
keepalived 2.0.12 官方:http://www.keepalived.org/ 一 简介 Keepalived is a routing software written in C. ...
- docker容器与宿主主机之间拷贝文件
文章链接:https://blog.csdn.net/libertine1993/article/details/80651552 https://blog.csdn.net/u011596455/a ...
- element-ui修改全局样式且只作用于当前页面
1)修改组件的样式,但是只作用于当前页面,其他页面不受影响,做法有两种: 法一:使用关键字“/deep/” 1)在当前页面添加样式: <style lang="scss" s ...
- 【winform】userControl刷新父窗体的datagridview
1.ContextMenuStrip 获取右键控件名称 this.contextMenuScriptScore.SourceControl.Name; //当前控件名 2.radiobutton 分组 ...
- jQuery的下拉选select2插件用法
1转自:https://www.jb51.net/article/95561.htm 用了这么久的Select2插件,也该写篇文章总结总结.当初感觉Select2不是特别好用,但又找不到比它更好的下拉 ...
- Ubuntu16.04安装VS Code
1. 官网下载VS Code安装包, 如名字为 vscodename.deb 2. 在 vscodename.deb 所在的文件夹打开 Terminal,输入 sudo dpkg -i vscoden ...
- C++线程中的几种锁
线程之间的锁有:互斥锁.条件锁.自旋锁.读写锁.递归锁.一般而言,锁的功能越强大,性能就会越低. 1.互斥锁 互斥锁用于控制多个线程对他们之间共享资源互斥访问的一个信号量.也就是说是为了避免多个线程在 ...
- spark DataFrame
DataFrame的推出,让Spark具备了处理大规模结构化数据的能力,不仅比原有的RDD转化方式更加简单易用,而且获得了更高的计算性能.Spark能够轻松实现从MySQL到DataFrame的转化, ...
- 将cookie 转换成字典格式
b = 'bid=Qzw9cKnyESM; ll="108288"; __yadk_uid=4YChvgeANLBEh4iV00n1tc0HQ8zpmSl1; __utmc=301 ...
- SQL Server Report Server 报表用户权限T-SQL查询
/************************************************************************** 查询用户在报表上的权限 ************ ...