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实现。

  1. class Resource {
  2.  
  3. public String name;
  4. public Integer sex;
  5. public boolean isCustomer = false;
  6. }
  7. // 生产者
  8. class Produces extends Thread {
  9.  
  10. private Resource resource;
  11.  
  12. Produces(Resource resource) {
  13. this.resource = resource;
  14. }
  15.  
  16. @Override
  17. public void run() {
  18. int count = 0;
  19. while(true) {
  20. synchronized (resource) {
  21. if(resource.isCustomer) {
  22. try {
  23. resource.wait();
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. if(count == 0) {
  29. resource.name = "produce";
  30. resource.sex = 0;
  31. } else {
  32. resource.name = "cusoumer";
  33. resource.sex = 1;
  34. }
  35. count = (count + 1) % 2;
  36. resource.isCustomer = true;
  37. resource.notify();
  38. }
  39. }
  40. }
  41. }
  42. // 消费者
  43. class Cusoumers extends Thread {
  44.  
  45. private Resource resource;
  46.  
  47. Cusoumers(Resource resource) {
  48. this.resource = resource;
  49. }
  50.  
  51. @Override
  52. public void run() {
  53. while(true) {
  54. synchronized (resource) {
  55. try {
  56. if(!resource.isCustomer) {
  57. resource.wait();
  58. }
  59. Thread.sleep(1000);
  60. } catch (InterruptedException e) {
  61. e.printStackTrace();
  62. }
  63. System.out.println("当前消费:" + resource.name + " " + resource.sex);
  64. resource.isCustomer = false;
  65. resource.notify();
  66. }
  67. }
  68. }
  69. }
  70.  
  71. public class WaitAndNotify {
  72.  
  73. public static void main(String[] args) {
  74.  
  75. Resource resource = new Resource();
  76.  
  77. Produces produce = new Produces(resource);
  78. Cusoumers cusoumer = new Cusoumers(resource);
  79. produce.start();
  80. cusoumer.start();
  81.  
  82. }
  83.  
  84. }

2.当在使用多线程时,线程间同时对某个共享的全局变量或者静态变量进行写操作时,将会发生数据冲突问题。

eg(1-1):

  1. @SuppressWarnings("static-access")
  2. public class MultiThread implements Runnable {
  3.  
  4. private Integer count = 100;
  5.  
  6. @Override
  7. public void run() {
  8.  
  9. while(count > 0) {
  10. try {
  11. Thread.currentThread().sleep(100);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. sale();
  16. }
  17. }
  18.  
  19. private void sale() {
  20. if(count > 0) {
  21. System.out.println("第" + (100 - count + 1) + "张票售出");
  22. count--;
  23. }
  24. }
  25.  
  26. public static void main(String[] args) {
  27.  
  28. MultiThread multiThread = new MultiThread();
  29.  
  30. Thread m1 = new Thread(multiThread, "1号窗口");
  31. Thread m2 = new Thread(multiThread, "2号窗口");
  32.  
  33. m1.start();
  34. m2.start();
  35. }
  36.  
  37. }

使用内置锁来处理(不推荐,性能比较低)

  1. private void sale() {
  2. synchronized (this) {
  3. if(count > 0) {
  4. System.out.println("第" + (100 - count + 1) + "张票售出");
  5. count--;
  6. }
  7. }
  8. }

2.ThreadLocal

eg(2-1):

  1. class Produce{
  2.  
  3. private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
  4. @Override
  5. protected Integer initialValue() {
  6.  
  7. return 0;
  8. }
  9. };
  10.  
  11. public Integer getCount() {
  12.  
  13. Integer nextCount = threadLocal.get() + 1;
  14. threadLocal.set(nextCount);
  15.  
  16. return nextCount;
  17. }
  18.  
  19. }
  20.  
  21. public class ThreadLocalTest extends Thread {
  22.  
  23. private Produce produce;
  24.  
  25. ThreadLocalTest(Produce produce) {
  26. this.produce = produce;
  27. }
  28.  
  29. @Override
  30. public void run() {
  31. for (int i = 0; i < 3; i++) {
  32. System.out.println(Thread.currentThread().getName() + "___" + produce.getCount());
  33. }
  34. }
  35.  
  36. public static void main(String[] args) {
  37.  
  38. Produce produce = new Produce();
  39. ThreadLocalTest t1 = new ThreadLocalTest(produce);
  40. ThreadLocalTest t2 = new ThreadLocalTest(produce);
  41. ThreadLocalTest t3 = new ThreadLocalTest(produce);
  42. t1.start();
  43. t2.start();
  44. t3.start();
  45.  
  46. }
  47.  
  48. }

编译器对代码进行重排序

  1. class Reorder {
  2.  
  3. int a = 0;
  4. boolean flag = false;
  5.  
  6. public void writer() {
  7. a = 1; // 当线程执行是a=1和flag=true时,编译器可能会先执行flag=true,然后在执行a=1
  8. flag = true;
  9. }
  10.  
  11. public void reader() {
  12. int i = 0;
  13. if(flag) {// 当线程执行是a=1和flag=true时,编译器可能会先执行i=a*a,然后在执行if(flag)
  14. i = a * a;
  15. System.out.println(i);
  16. }
  17. }
  18. }
  19.  
  20. class ReorderThread1 extends Thread {
  21.  
  22. private Reorder reorder;
  23.  
  24. ReorderThread1(Reorder reorder) {
  25. this.reorder = reorder;
  26. }
  27.  
  28. @Override
  29. public void run() {
  30. reorder.writer();
  31. }
  32.  
  33. }
  34.  
  35. public class ReorderThread extends Thread {
  36.  
  37. private Reorder reorder;
  38.  
  39. ReorderThread(Reorder reorder) {
  40. this.reorder = reorder;
  41. }
  42.  
  43. @Override
  44. public void run() {
  45. reorder.reader();
  46. }
  47.  
  48. public static void main(String[] args) {
  49.  
  50. Reorder reorder = new Reorder();
  51. ReorderThread t1 = new ReorderThread(reorder);
  52. ReorderThread1 t2 = new ReorderThread1(reorder);
  53.  
  54. t1.start();
  55. t2.start();
  56.  
  57. }
  58. }

3.读写锁

  1. public class ReadWriteLockTest {
  2.  
  3. public static Map<Integer, Integer> maps = new HashMap<>();
  4. public static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  5.  
  6. public static Lock readLock = readWriteLock.readLock();
  7. public static Lock writeLick = readWriteLock.writeLock();
  8.  
  9. public static Integer getResult(Integer key) {
  10.  
  11. readLock.lock();
  12. Integer result = null;
  13. try {
  14. System.out.println("=======读=======");
  15. Thread.currentThread().sleep(50);
  16. result = maps.get(key);
  17. System.out.println(key);
  18. System.out.println("=======读结束=======");
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. } finally {
  22. readLock.unlock();
  23. }
  24.  
  25. return result;
  26. }
  27.  
  28. public static void putResult(Integer key, Integer value) {
  29.  
  30. writeLick.lock();
  31. try {
  32. System.out.println("=======写=======");
  33. Thread.currentThread().sleep(50);
  34. maps.put(key, value);
  35. System.out.println(key + "_" + value);
  36. System.out.println("=======写结束=======");
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. } finally {
  40. writeLick.unlock();
  41. }
  42.  
  43. }
  44.  
  45. public static void main(String[] args) {
  46.  
  47. new Thread(new Runnable() {
  48.  
  49. @Override
  50. public void run() {
  51. for (int i = 0; i < 100; i++) {
  52. putResult(i, i);
  53. }
  54. }
  55. }).start();
  56.  
  57. new Thread(new Runnable() {
  58.  
  59. @Override
  60. public void run() {
  61. for (int i = 0; i < 100; i++) {
  62. getResult(i);
  63. }
  64. }
  65. }).start();
  66. }
  67. }

若屏蔽掉读写锁,如下

3. CAS源码

  1. public final int getAndAddInt(Object o, long offset, int delta) {
  2. int v;
  3. do {
  4. v = getIntVolatile(o, offset);
  5. } while (!compareAndSwapInt(o, offset, v, v + delta));
  6. return v;
  7. }
  8.  
  9. /**
  10. * Atomically increments by one the current value.
  11. *
  12. * @return the updated value
  13. */
  14. public final int incrementAndGet() {
  15. for (;;) {
  16. //获取当前值
  17. int current = get();
  18. //设置期望值
  19. int next = current + 1;
  20. //调用Native方法compareAndSet,执行CAS操作
  21. if (compareAndSet(current, next))
  22. //成功后才会返回期望值,否则无线循环
  23. return next;
  24. }
  25. }

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. C++入门篇二

    c++是c语言的增强版,但是和c语言之间有何区别呢? c和c++的区别: 1.全局变量检测增强int a;int a=10; 2.函数检测增强,参数类型增强,返回值检测增强,函数调用参数检测增强(参数 ...

  2. 检测浏览器是否支持ES6

    这是阮一峰老师写的一个工具 ES-Checker,用来检查各种运行环境对 ES6 的支持情况.访问ruanyf.github.io/es-checker,可以看到您的浏览器支持 ES6 的程度.运行下 ...

  3. 浅谈z-index

    z-index使用条件 CSS3之前,z-index属性只有和定位元素在(postion不为static的元素)一起的时候才会有作用,但CSS3中flex盒子的子元素也可以设置z-index.理论上来 ...

  4. css实现右尖括号样式

    .arrow{ width: 6px; height: 6px; border-top: 1px solid #999; border-right: 1px solid #999; transform ...

  5. OpenCV-Python教程9-平滑图像

    先解释一个单词 blur:使...模糊不清 滤波与模糊 滤波和模糊都属于卷积,不同的滤波方法之间只是卷积核不同(对线性滤波而言) 低通滤波器是模糊,高通滤波器是锐化 低通滤波器允许低频信号通过,在图像 ...

  6. python TextMining

    01.Crawling url_request # -*- coding: utf-8 -*- """ Created on Sun Feb 17 11:08:44 20 ...

  7. SOUI taobao SVN目录结构说明

  8. Hbase合并Region的过程中出现永久RIT的解决

    在合并Region的过程中出现永久RIT怎么办?笔者在生产环境中就遇到过这种情况,在批量合并Region的过程中,出现了永久MERGING_NEW的情况,虽然这种情况不会影响现有集群的正常的服务能力, ...

  9. 机器学习入门之python实现图片简单分类

    小任务:实现图片分类 1.图片素材 python批量压缩jpg图片: PIL库 resize http://blog.csdn.net/u012234115/article/details/50248 ...

  10. [CF542A]Place Your Ad Here

    [CF542A]Place Your Ad Here 题目大意: 有\(n(n\le2\times10^5)\)个广告和\(m(m\le2\times10^5)\)个电视台,第\(i\)个广告只能在\ ...