实习日记 08/03 day19 理解JavaWeb--线程并发

总览

在这里插入图片描述

胡闹厨房和Java并发:

玩过胡闹厨房的都知道,就像处理器一时间只能处理一件事情一样,厨师也是如此,做一道菜需要的步骤有些如切菜、洗盘子需要人工一直执行,但是像煮面蒸米等这些耗时操作,厨师是不需要在旁边处理的。对于计算机也是一样,很多时候,需要让计算机同时去做几件事,不要出现煮面时在旁边干看着的情况出现,计算机的运算速度可比胡闹厨房的肌无力厨师切菜快多了,但是存储和通信子系统的速度就像煮面一样缓慢,如果不希望大部分时间都处于等待其他资源的状态下,就要让计算机同时处理几项任务,来实现效率最大化,也是胡闹厨房单人满星的必备技巧。

本篇文章将会从Java并发底层虚拟机入手,一层一层向上理解Java并发,最后会转到Android的线程并发实例。

Java中的并发

Java内存模型与线程

Java虚拟机定义一种Java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异,实现Java程序在各种平台都能达到一致的内存访问效果。

Java内存模型

在这里插入图片描述
个人理解:
线程都应有自己对应的工作内存,还有数据存放的主内存,工作内存和主内存之间的交互需要通过八大操作来实现,而对于这些操作还有八大规则来实现正常的读取数据。实际上工作内存上存储的数据只是主内存的一个副本。工作内存和主内存可以相互交互
假如A修改了某个变量,如何保证B可见,这就需要Volatile操作。Volatile执行的是新的规则,将use和load、store和assign捆绑起来,同时让线程不会被指令重排序优化。
理解JMM,首先要知道Happens-before原则

  1. 程序顺序规则:每一个线程中的操作,happens-before于该线程中的任意后续操作
  2. 监视器锁规则:对于一个锁的解锁,happens-before与随后对这个锁的解锁
  3. volitile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读
  4. 传递性:如果A h-b 于B,而B h-b 于C,那肯定A h-b C
  5. start()规则:如果线程A执行操作开启线程B,那么A中的开启B操作先于B中任意操作
  6. join()规则:如果A执行操作B.join()并成功返回,那么线程B中的任意操作h-b与A从b.join的返回

思维导图太糊了,如有需要可以私信我。

Java与线程

在这里插入图片描述

java中的线程调度是抢占式的,但是可以通过使用优先级来保证某些重要线程的正常工作。

Java线程的三种创建方式

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadA {
    static class ThreadOne extends Thread{
        @Override
        public void run() {
            System.out.println("今宵明月");
        }

    }
    static class ThreadTwo implements Runnable{

        @Override
        public void run() {
            System.out.println("绝不西沉");
        }
    }
    static class ThreadThree implements Callable{
        @Override
        public String call() throws Exception {
            return "只此美梦,不在苏醒";
        }
    }

    public static void main(String[] args) {
        ThreadOne t1=new ThreadOne();
        ThreadTwo t2=new ThreadTwo();
        ThreadThree t3=new ThreadThree();
        Thread t=new Thread(t2);
        t1.run();
        t.start();
        FutureTask<String> ft = new FutureTask<>(t3);
        t=new Thread(ft);
        t.start();
        try
        {
            System.out.println(ft.get());
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        } catch (ExecutionException e)
        {
            e.printStackTrace();
        }

    }
}

  1. 继承Thread类,实现run()方法
  2. 实现Runnable接口
  3. 实现Callable接口获取返回值,利用Future获取返回值完成

线程的生命周期

  1. 新建
    使用New关键字创建一个线程后该线程进入到新建状态,JVM为其分配内存,并初始化其成员变量
  2. 就绪
    调用start方法后,该线程进入到就绪状态,JVM虚拟机为其穿件方法调用栈和程序计数器,等待调度使用
  3. 运行
    处于就绪状态的线程,获取了CPU可以运行run()中的方法
  4. 阻塞
    阻塞状态是指线程因为某种原因放弃了CPU使用权,三种阻塞情况:等待阻塞,同步阻塞,其他阻塞
  5. 死亡
    正常结束,异常结束或者调用stop(别用),interrupt结束。

线程安全与锁优化

就像数据库面临着:脏读、幻读、不可重复读这些难题一样。当多线程情况下,线程对数据的操作也可能导致这些问题的产生。数据库为了解决这三大难题提供了四种隔离等级,每向上一层就多解决三大问题之一。那么类比着数据库,java也有保证自己线程安全的机制。
当多线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。
在这里插入图片描述

在这里插入图片描述
在JavaSE1.6中,锁一共有4种状态,级别从高到低依次是无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态。锁可以升级但是不可以降级,这种机制提高了获得锁和释放锁的效率。

优点缺点使用场景
偏向锁加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级差距如果线程间存在锁竞争,会带来额外的所撤销的消耗只有一个线程访问同步块场景
轻量级锁竞争的线程不会阻塞,提高了程序的响应速度如果持续得不到锁竞争的线程,会使用自旋消耗CPU追求响应速度,同步块执行非常快
重量级锁线程竞争不使用自旋,不会消耗CPU线程阻塞,响应时间缓慢追求吞吐量,同步块执行速度较长

个人对锁的认识

无论是深入理解Jvm还是Java并发的艺术对于锁这个概念的描述都不全面,而且难以理解,以下是我的总结和理解。
就像数据库的四个隔离是为了解决三个问题而提出来的,而Java线程的锁结构也应该是为了保证线程在操纵数据的过程中出现各种问题而提出来的。
主内存相当于一个箱子,里面存着各种颜色的小球,所有人无序的随机拿起小球,并涂上自己喜欢的颜色在放入箱子中,其他人再拿起来图上自己喜欢的颜色,拿起的过程属于锁,放下的过程属于锁释放。如果Java并发像这样有序就舒服了,但通常情况是:

import java.util.Random;

public class Buy {
    public static int count=10;
    static Thread buy1=new Thread(new Runnable() {

        @Override
        public void run() {

            while(count>0){
                System.out.println("现有余票"+count);
                try {
                    Thread.sleep(new Random().nextInt(1000));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("A买一张,还有:"+(--count));
            }

        }
    });
    static Thread buy2=new Thread(new Runnable() {
        @Override
        public void run() {

            while(count>0){
                System.out.println("现有余票"+count);
                try {
                    Thread.sleep(new Random().nextInt(1000));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("B买一张,还有:"+(--count));
            }

        }
    });
    static Thread buy3=new Thread(new Runnable() {
        @Override
        public void run() {
            while(count>0){
                System.out.println("现有余票"+count);
                try {
                    Thread.sleep(new Random().nextInt(1000));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("C买一张,还有:"+(--count));
            }
        }
    });

    public static void main(String[] args) {
        buy1.start();
        buy2.start();
        buy3.start();
    }
}

在这里插入图片描述
很明显,在线程B,C拿到count时是大于0的,此时BC并没有买票,而是通过sleep()暂停了后续处理,此时票被其他线程买走后,BC醒来,继续执行买票操作,造成count成为了负数。正确的流程应该是判定完是否表有余之后,后续线程不可操纵count,等该线程买过票后在继续其他线程买票。
解决办法如下:
当一个线程要使用火车票这个资源,可以给它一把锁,等它把事情做完后再把锁给另一个要用这个资源的线程。

在这里插入图片描述

Synchronized同步锁

Synchronized可以把任意一个非null的对象当做锁,属于独占式的悲观锁,同时属于可重入锁

Synchronized作用范围

1.作用于方法时,锁住的是对象的实例(this)
2.当作用于静态方法时,锁住的是Class实例,又因为Class的相关数据存储在永久区metaspace,是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁住调用该方法的线程
3.synchronized作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。

Synchronized核心组件

1.Wait Set:哪些调用wait方法被阻塞的线程被放置在这里
2.Content List:竞争队列,所有请求锁的线程首先被放在这个竞争队列中
3.Entry List:有资格称为竞争队列的线程被移动到Entry List
4.OnDeck:任意时刻,最多只有一个线程正在竞争锁资源
5.Owner:当前以获取到的所有资源的线程Owner
6.!Owner:当前释放锁的线程

ReentrantLock

ReentrantLock继承接口Lock并实现了接口中定义的方法,它是一种可重入锁,除了能完成synchronized的所有工能,还可以实现可响应式中断锁,可轮训锁请求,定时锁等。

具体的可以参考这个博主的博文,写的非常好(链接: .)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值