• 震撼来袭!京东架构师手写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、分布式缓存、数据结构等等。

  • 相关阅读:
    xls与csv文件的区别
    青音,经典爱情语录
    win7用户账户自动登录方法汇总
    How to using Procedure found Lead Blocker
    FTS(3) BSD 库函数手册 遍历文件夹(二)
    FTS(3) BSD 库函数手册 遍历文件夹(一)
    DisplayMetrics类 获取手机显示屏的基本信息 包括尺寸、密度、字体缩放等信息
    About App Distribution 关于应用发布
    FTS(3) 遍历文件夹实例
    OpenCV 2.1.0 with Visual Studio 2008
  • 原文地址:https://www.cnblogs.com/lwh1019/p/14483155.html
Copyright © 2020-2023  润新知