Java多线程中相关术语:

  一、Java多线程原则

    1.原子性:一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。一般使用线程同步或者Lock锁来确保。

    2.可见性(Java内存模型):当多个线程同时访问一个变量时,其中某个线程改变改变量值,其他线程需要立即查询到。

    3.有序性(涉及JVM对代码的重排序问题):编译器和处理器对不影响逻辑的代码进行重排序(执行顺序),以便提高运行速率(涉及Java的内存屏障,可以使用volatile来禁止重排序)。

备注:指令重排序时不能把后面的指令重排序到内存屏障之前的位置。

   二、Java内存模型(JMM)

    1.JMM决定了一个线程对共享变量进行操作后,能否对其他线程可见。

    2.JMM为每个线程抽象出一个本地内存概念,让一个线程对共享变量进行修改时,先修改本地内存中的值,然后在将本地内存中的值刷新到主线程中供其他线程读取。

       其中volatile关键字修饰的变量,会被立即更新到主内存中,供其他线程所调用。

       备注(volatite的作用域):在Java中为了加快程序的运行效率,对变量的操作是在寄存器或者CPU上,之后在同步到主内存中,使用volatile修饰后会立即同步至主内存中。   

Java 中的两种线程:

  1. 守护线程 : 主线程停止后,守护线程会立即停止( 例如:GC 线程) ,Java 中使用setDaemon(true) 方法来设置为守护线程。
  2. 用户线程 : 用户自定义的线程,当主线程停止后,用户不会立即停止。

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多线中基础知识整理的更多相关文章

  1. java部分基础知识整理----百度脑图版

    近期发现,通过百度脑图可以很好的归纳总结和整理知识点,本着学习和复习的目的,梳理了一下java部分的知识点,不定期更新,若有不恰之处,请指正,谢谢! 脑图链接如下:java部分基础知识整理----百度 ...

  2. Kali Linux渗透基础知识整理(二)漏洞扫描

    Kali Linux渗透基础知识整理系列文章回顾 漏洞扫描 网络流量 Nmap Hping3 Nessus whatweb DirBuster joomscan WPScan 网络流量 网络流量就是网 ...

  3. 《Java核心技术·卷Ⅰ:基础知识(原版10》学习笔记 第5章 继承

    <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 目录 <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 5.1 类.超类和子类 5.1 ...

  4. Kali Linux渗透基础知识整理(四):维持访问

    Kali Linux渗透基础知识整理系列文章回顾 维持访问 在获得了目标系统的访问权之后,攻击者需要进一步维持这一访问权限.使用木马程序.后门程序和rootkit来达到这一目的.维持访问是一种艺术形式 ...

  5. 【OGG】OGG基础知识整理

    [OGG]OGG基础知识整理 一.GoldenGate介绍 GoldenGate软件是一种基于日志的结构化数据复制软件.GoldenGate 能够实现大量交易数据的实时捕捉.变换和投递,实现源数据库与 ...

  6. wifi基础知识整理

    转自 :http://blog.chinaunix.net/uid-9525959-id-3326047.html WIFI基本知识整理 这里对wifi的802.11协议中比较常见的知识做一个基本的总 ...

  7. JavaScript基础知识整理

    只整理基础知识中关键技术,旨在系统性的学习和备忘. 1.在 JScript 中 null 和 undefined 的主要区别是 null 的操作象数字 0,而 undefined 的操作象特殊值NaN ...

  8. 如何看K线图基础知识

    在日K线图中一般白线.黄线.紫线.绿线依次分别表示:5.10.20.60日移动平均线,但这并不是固定的,会根据设置的不同而不同,比如你也可以在系统里把它们设为5.15.30.60均线. 你看K线图的上 ...

  9. Java学习之旅基础知识篇:数据类型及流程控制

    经过开篇对Java运行机制及相关环境搭建,本篇主要讨论Java程序开发的基础知识点,我简单的梳理一下.在讲解数据类型之前,我顺便提及一下Java注释:单行注释.多行注释以及文档注释,这里重点强调文档注 ...

随机推荐

  1. web开发简史与技术选型

    视频地址:http://v.youku.com/v_show/id_XMTQxNzM1MzAwOA==.html?firsttime=0&from=y1.4-2

  2. spring集成Junit做单元测试及常见异常解决办法

    spring-test依赖包 <!--Spring-test --> <!-- https://mvnrepository.com/artifact/org.springframew ...

  3. TCP 的那些事儿

    TCP是一个巨复杂的协议,因为他要解决很多问题,而这些问题又带出了很多子问题和阴暗面.所以学习TCP本身是个比较痛苦的过程,但对于学习的过程却能让人有很多收获.关于TCP这个协议的细节,我还是推荐你去 ...

  4. 三目算法、if/else,switch/case运用

    //输入学生的成绩,判断考试是否及格,及格6大于等于0 //第一种写法:三目运算 大多用于单独判断是否满足某个条件 import java.util.Scanner; public class Hel ...

  5. iOS -- Effective Objective-C 阅读笔记 (9)

    // 将类的实现方法代码反三到便于管理的数个分类之中.        // 类中经常容易填满各种方法, 而这些方法的代码则全部堆在一个巨大的实现文件中, 有时这么做事不合理的,因为即使通过重构把这个类 ...

  6. Windows Internals 笔记——进程的权限

    1.大多数用户都用一个管理员账户来登录Windows,在Vista之前,这样的登录会创建一个安全令牌.每当有代码试图使用一个受保护的安全资源时,操作系统就会出示这个令牌.从包括Windows资源管理器 ...

  7. C# .Net String字符串效率提高-字符串拼接

    字符串操作是编程中非常频繁的操作,特别是在拼接字符串的时候.下面来说说字符串拼接的效率提升. 1. 减少装箱 值类型与引用类型之间的转换存在装箱与拆箱操作:将值类型转换成引用类型的操作叫装箱,将引用类 ...

  8. Linux安装Tomcat-Nginx-FastDFS-Redis-Solr-集群——【第六集之基本命令使用】

    学习命令的方法:linux中所有操作都是命令操作,可想而知命令有多少,更严重的是每个命令有很多参数,记命令容易,记参数就难了,所以建议: 自己准备一个博客,把通常用到的命令及其功能记载下来,用到的时候 ...

  9. 思维导图工具XMind

    思维导图工具XMind XMind简单介绍 官网地址:https://www.xmind.cn/ XMind 是一个全功能的思维导图和头脑风暴软件,为激发灵感和创意而生.作为一款有效提升工作和生活效率 ...

  10. [PKUSC2018]星际穿越

    [PKUSC2018]星际穿越 题目大意: 有一排编号为\(1\sim n\)的\(n(n\le3\times10^5)\)个点,第\(i(i\ge 2)\)个点与\([l_i,i-1]\)之间所有点 ...