zoukankan      html  css  js  c++  java
  • 震撼来袭!京东架构师手写JUC技术笔记,看过的人都说好!

    什么是JUC

    在Java中,线程部分是一个重点,本篇文章说的JUC也是关于线程的。JUC就是java.util .concurrent工具包的简称。这是一个处理线程的工具包,JDK 1.5开始出现的。

    进程与线程

    进程

    进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。

    线程

    线程(thread) 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

    总结

    进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。

    线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。

    对于Java而言:Thread、Runnable、Callable

    扩展:Java 真的可以开启线程吗?

     public synchronized void start() {
            /**
             * This method is not invoked for the main method thread or "system"
             * group threads created/set up by the VM. Any new functionality added
             * to this method in the future may have to also be added to the VM.
             *
             * A zero status value corresponds to state "NEW".
             */
            if (threadStatus != 0)
                throw new IllegalThreadStateException();
    
            /* Notify the group that this thread is about to be started
             * so that it can be added to the group's list of threads
             * and the group's unstarted count can be decremented. */
            group.add(this);
    
            boolean started = false;
            try {
                start0();
                started = true;
            } finally {
                try {
                    if (!started) {
                        group.threadStartFailed(this);
                    }
                } catch (Throwable ignore) {
                    /* do nothing. If start0 threw a Throwable then
                      it will be passed up the call stack */
                }
            }
        }
    	// 调用本地方法区 调用c++方法,所以说java不能启动线程
        private native void start0();
    
    

    并发与并行

    并发编程的本质:充分利用CPU的资源

    并发

    并发(多线程操作同一个资源) CPU 一核 ,模拟出来多条线程,天下武功,唯快不破,快速交替

    并行

    多个人一起行走 CPU 多核 ,多个线程可以同时执行; 线程池

    总结

    并发:不同的代码块交替执行

    并行:不同的代码块同时执行

    线程状态

    public enum State {
    // 新生
    NEW,
    // 运行
    RUNNABLE,
    // 阻塞
    BLOCKED,
    // 等待,死死地等
    WAITING,
    // 超时等待
    TIMED_WAITING,
    // 终止
    TERMINATED;
    }
    
    

    wait/sleep 区别

    • 来自不同的类 wait => Object sleep => Thread
    • 关于锁的释放 wait 会释放锁,sleep 睡觉了,抱着锁睡觉,不会释放!
    • 使用的范围是不同的 wait 必须在同步代码块, sleep 可以再任何地方
    • 是否需要捕获异常 wait 不需要捕获异常 sleep 必须要捕获异常

    三 Lock锁(重点)

    传统 Synchronized

    package com.shu;
    
    public class SaleTicketDemo01 {
        public static void main(String[] args) {
            // 并发:多线程操作同一个资源类, 把资源类丢入线程
            Ticket ticket = new Ticket();
            // @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
            new Thread(()->{
                for (int i = 1; i < 40 ; i++) {
                    ticket.sale();
                }
            },"A").start();
            new Thread(()->{
                for (int i = 1; i < 40 ; i++) {
                    ticket.sale();
                }
            },"B").start();
            new Thread(()->{
                for (int i = 1; i < 40 ; i++) {
                    ticket.sale();
                }
            },"C").start();
        }
    }
    
    // 资源类 OOP
    class Ticket {
        // 属性、方法
        private int number = 30;
        // 卖票的方式
    // synchronized 本质: 队列,锁
        public synchronized void sale(){
            if (number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余:"+number);
            }
        }
    }
    
    

    特性

    • 原子性: 所谓原子性就是指一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行,注意!面试时经常会问比较synchronized和volatile,它们俩特性上最大的区别就在于原子性,volatile不具备原子性。
    • 可见性:可见性是指多个线程访问一个资源时,该资源的状态、值信息等对于其他线程都是可见的。
    • 有序性: 有序性值程序执行的顺序按照代码先后执行。
    • 可重入性: 通俗一点讲就是说一个线程拥有了锁仍然还可以重复申请锁。

    Synchronized 底层原理

    • 在理解锁实现原理之前先了解一下Java的对象头和Monitor(监控),在JVM中,对象是分成三部分存在的:对象头、实例数据、对其填充。

    • 实例数据和对其填充与synchronized无关,这里简单说一下(我也是阅读《深入理解Java虚拟机》学到的,读者可仔细阅读该书相关章节学习)。实例数据存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐;对其填充不是必须部分,由于虚拟机要求对象起始地址必须是8字节的整数倍,对齐填充仅仅是为了使字节对齐。

    • 对象头是我们需要关注的重点,它是synchronized实现锁的基础,因为synchronized申请锁、上锁、释放锁都与对象头有关。对象头主要结构是由Mark Word 和 Class Metadata Address组成,其中Mark Word存储对象的hashCode、锁信息或分代年龄或GC标志等信息,Class Metadata Address是类型指针指向对象的类元数据,JVM通过该指针确定该对象是哪个类的实例。

    • 锁也分不同状态,JDK6之前只有两个状态:无锁、有锁(重量级锁),而在JDK6之后对synchronized进行了优化,新增了两种状态,总共就是四个状态:无锁状态、偏向锁、轻量级锁、重量级锁,其中无锁就是一种状态了。锁的类型和状态在对象头Mark Word中都有记录,在申请锁、锁升级等过程中JVM都需要读取对象的Mark Word数据。

    • 每一个锁都对应一个monitor对象,在HotSpot虚拟机中它是由ObjectMonitor实现的(C++实现)。每个对象都存在着一个monitor与之关联,对象与其monitor之间的关系有存在多种实现方式,如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个monitor被某个线程持有后,它便处于锁定状态。

    ObjectMonitor() {
       _header       = NULL;
       _count        = 0;  //锁计数器
       _waiters      = 0,
       _recursions   = 0;
       _object       = NULL;
       _owner        = NULL;
       _WaitSet      = NULL; //处于wait状态的线程,会被加入到_WaitSet
       _WaitSetLock  = 0 ;
       _Responsible  = NULL ;
       _succ         = NULL ;
       _cxq          = NULL ;
       FreeNext      = NULL ;
       _EntryList    = NULL ; //处于等待锁block状态的线程,会被加入到该列表
       _SpinFreq     = 0 ;
       _SpinClock    = 0 ;
       OwnerIsThread = 0 ;
     }
    
    

    Lock 接口

    package com.shu;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class SaleTicketDemo02{
        public static void main(String[] args) {
            // 并发:多线程操作同一个资源类, 把资源类丢入线程
            Ticket ticket = new Ticket();
    
            // @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
            new Thread(()->{for (int i = 1; i < 40 ; i++)
                ticket.sale();},"A").start();
            new Thread(()->{for (int i = 1; i < 40 ; i++)
                ticket.sale();},"B").start();
            new Thread(()->{for (int i = 1; i < 40 ; i++)
                ticket.sale();},"C").start();
        }
    }
    
    // 资源类 OOP
    class Ticket02{
        // 属性、方法
        private int number = 30;
        // 创建锁
        Lock lock=new ReentrantLock();
        public  void sale(){
            //上锁
            lock.lock();
            try {
            if (number>0){
                    System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余:"+number);
                }
            }catch (Exception e)
            {
                e.printStackTrace();
            }
            finally {
                //解锁
                lock.unlock();
            }
    
        }
    }
    
    

    对比

    • Synchronized 内置的Java关键字, Lock 是一个Java类
    • Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
    • Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
    • Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下 去
    • Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以 自己设置)
    • Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!

    最后

    欢迎关注公众号:前程有光,领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结! 这些资料的内容都是面试时面试官必问的知识点,篇章包括了很多知识点,其中包括了有基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。

  • 相关阅读:
    hdu 4476 Cut the rope (2-pointer && simulation)
    hdu 1286 找新朋友 (容斥原理 || 欧拉函数)
    函数存储的另一种思路
    grunt 入门 应用grunt对代码进行压缩
    sublime flatLand 主题
    如何应用r.js对requirejs下的js代码合并
    如何在requirejs下引用bootstrap
    学习在requirejs下如何使用underscore.js模板
    应用js函数柯里化currying 与ajax 局部刷新dom
    js 函数arguments一种用法
  • 原文地址:https://www.cnblogs.com/lwh1019/p/14483155.html
Copyright © 2011-2022 走看看