1.基础知识

  • java默认的两个线程:Main线程+GC守护线程

  • java并不能开启线程,需要调用底层用c语言写的本地方法

  • wait和sleep的区别:

    wait方法会释放线程锁,并且只能在同步代码块中使用,sleep带锁睡眠,可以在任一地方睡眠

  • Synchronized锁和lock锁的区别

    Synchronized会自动释放锁,lock需手动释放,不然会造成死锁

    Synchronized线程会持续等待直到获得锁,而lock锁的tryLock()方法避免了死等

    Synchronized(可重入锁、公平锁、非中断锁)

    Lock(可重入锁、默认非公平锁(可设置公平)、可中断锁)

  • java对象布局

    1.对象的实例属性

    2.对象头(12byte)

    • MarkWord
    • Class Metadata Address(Class Pointer)

    3.数据对齐 (1+2的总大小不是8byte的倍数使用于补齐)

    好博客分享:[https://blog.csdn.net/Mr_wxc/article/details/107710945?utm_medium=distribute.pc_relevant.none-task-blog-2~default~OPENSEARCH~default-16.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~OPENSEARCH~default-16.control]

  • Condition监视器

    与lock配套为了代替原wait和notify

    Condition con = lock.newCondition();//创建监视器对象
    con.await();//线程等待
    con.signal();//线程唤醒

    condition监视器可以创建多个监视器对象同时监视多个线程,可以达到控制线程执行的效果

  • 集合的线程不安全

    线程不安全的集合在进行线程修改时会几率报出并发修改异常ConcurrentModificationException

    线程安全的集合:Vector、Hashtable、ConcurrentHashMap、Stack

    集合线程不安全的解决办法

    1.用线程安全的集合替代

    2.用Collections.synchronized+集合名系列集合,如:

    List list = Collections.synchronizedList(new ArrayList<>());

    3.用CopyOnWrite系列集合(写时复制)

    相比1、2方法,方法3使用的是lock锁,效率要高于synchronized锁,其次写时复制的意思是多个线程修改的是原集合的副本,在修改完成后再写回原集合,所以lock锁是加在副本上的,原集合此时依然可以被只读线程获取,加快了读写效率,代价是副本内存占用和数据实时性。

    map集合没有CopyOnWrite,但有一个等效的ConcurrentHashMap

    这里推荐一个CSDN博主!(https://blog.csdn.net/weixin_44460333/article/details/86770169)

  • 常用辅助类

    CountDownLatch 减法计数器

    CountDownLatch latch = new CountDownLatch(10);//初始化为10
    for (int i = 0; i < 10; i++) {
    new Thread(()->{
    latch.countDown();//计数器减一
    }).start();
    }
    latch.await();//等待计数器归零
    System.out.println("执行完毕");

    CyclicBarrier 线程加计数器

     CyclicBarrier barrier = new CyclicBarrier(10,()->{
    System.out.println("顶级线程执行");//线程计数达到10之后执行该线程
    });
    for (int i = 0; i < 10; i++) {
    new Thread(()->{
    barrier.await();//线程计数器加一
    }).start();
    }

    Semaphore信号量

    Semaphore semaphore = new Semaphore(5);
    //同一时间内只能有5个线程“执行”,并发限流
    for (int i = 0; i < 10; i++) {
    new Thread(()->{
    try {
    semaphore.acquire();
    System.out.println(Thread.currentThread().getName()+"抢到车位");
    TimeUnit.SECONDS.sleep(5);
    semaphore.release();
    System.out.println(Thread.currentThread().getName()+"离开车位");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    },""+i).start();
  • ReadWriteLock 读写锁

        private ReadWriteLock lock = new ReentrantReadWriteLock();
    //读锁
    lock.readLock().lock();
    ...//业务代码
    lock.readLock().unlock();
    //写锁
    lock.writeLock().lock();
    ...//业务代码
    lock.writeLock().unlock();
    }
  • Blocking Queue 阻塞队列

    • 队列的四组API
    方法功能 抛出异常 不抛出异常,返回值 阻塞等待 超时等待
    添加元素 add() offer() put 重载offer
    删除元素 remove() poll() take 重载poll
    判断队列头 elment() peek()

    重载offer(Object,long timeOut(等待时间),TimeUnit(时间单位))

    重载poll(long timeOut(等待时间),TimeUnit(时间单位))

    • SynchronousQueue同步队列

    队列中只能有一个元素,当队列中有元素时,不允许添加其他元素,只有当该元素被移除,才能继续添加

2.线程池

  • Executors

    ExecutorService threadpool = Executors.newCachedThreadPool();//伸缩池
    //ExecutorService threadpool = Executors.newFixedThreadPool(5);//固定大小的池
    //ExecutorService threadpool = Executors.newSingleThreadExecutor();//单个线程的池
    for (int i = 0; i < 100; i++) {
    threadpool.execute(()->{//创建线程
    System.out.println(Thread.currentThread().getName()+" is Running");
    });
    }
    threadpool.shutdown();
    //一般不使用executors创建线程池,高并发下容易报出oom
  • 七大参数

    public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
    int maximumPoolSize,//最大线程数量
    long keepAliveTime,//线程存活时间
    TimeUnit unit,//时间单位
    BlockingQueue<Runnable> workQueue,//阻塞队列
    ThreadFactory threadFactory,//线程工厂
    RejectedExecutionHandler handler) {//拒绝策略
    ...
    }
    ```
  • 四种拒绝策略

    ThreadPoolExecutor executor = new ThreadPoolExecutor(3,5,2,//自定义线程池
    TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),
    Executors.defaultThreadFactory(),
    //拒绝策略
    //new ThreadPoolExecutor.AbortPolicy()//阻塞队列满,抛出异常
    //new ThreadPoolExecutor.CallerRunsPolicy()//由原调用线程执行(哪来回哪去)
    //new ThreadPoolExecutor.DiscardPolicy()//阻塞队列满,不会抛出异常
    new ThreadPoolExecutor.DiscardOldestPolicy()//阻塞队列满,不会抛出异常,和最早进入阻塞队列的线程竞争
    );
  • 最大线程数该如何定义

    cpu密集型,最大线程数=内核数

    Runtime.getRuntime().availableProcessors();//获取计算机核数

    io密集型,最大线程数>占用IO大的线程数

3.接口

  • 四大函数式接口

    public interface Function<T, R> {
    //输入T型,返回R型
    R apply(T t);//需实现apply方法
    }
    public interface Predicate<T> {
    //输入T型,返回布尔型
    boolean test(T t);//需实现test方法
    }//断定型接口
    public interface Consumer<T> {
    //只有输入、没有输出
    void accept(T t);//需实现accept方法
    }//消费型接口
    public interface Supplier<T> {
    //只有返回值,没有输入
    T get();//需实现get方法
    }//供给型接口
  • Stream流式计算

    //存储交给集合,计算交给流
    list.stream()
    .filter(u->{return u.getId()%2==0;})//筛选偶数id的用户
    .filter(u->{return u.getAge()>22;})//筛选年龄大于22的用户
    .map(u->{return u.getName().toUpperCase();})//将用户名转换为大写
    .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})//将用户名倒序排序
    .limit(1)//限制输出个数为1
    .forEach(System.out::println);//便利打印

4.JMM

  • JMM内存模型

  • 内存交互八大操作及其约束

    1.lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态

    2.unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

    3.read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用

    4.load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中

    5.use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令

    6.assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中

    7.store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用

    8.write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

    JMM对这八种指令的使用,制定了如下规则:

    1.不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write

    2.不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存

    3.不允许一个线程将没有assign的数据从工作内存同步回主内存

    4.一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作

    5.一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁

    6.如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值

    7.如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量

    8.对一个变量进行unlock操作之前,必须把此变量同步回主内存

    来自【https://zhuanlan.zhihu.com/p/29881777】

  • volatile关键字

    1.保证线程可见性,不保证原子性(保证原子性可以用Lock、synchronized、Semaphore(信号量)、原子类(java.util.concurrent.atomic))

    2.禁止指令重排(内存屏障)

  • CAS

    CAS(Compare And Swap)比较并替换。

    CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。

    更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B,预期值不一样时,将会通过循环获取预期值进行CAS(底层用自旋锁实现)。

    CAS会导致ABA问题(即内存地址中的值可能在该线程执行过程中被修改之后再改回来,导致CAS提交时预期值和内存地址中的值相同,修改成功)

    可以通过原子引用解决这个问题:

    AtomicStampedReference<T> stampedReference =
    new AtomicStampedReference(T,version版本号);
    stampedReference.compareAndSet(T_预期值,T_修改值,原始版本号,新版本号);
    stampedReference.getReference();//获取当前版本号

Java基础篇——JUC初步的更多相关文章

  1. java基础篇---I/O技术

    java基础篇---I/O技术   对于任何程序设计语言而言,输入输出(I/O)系统都是比较复杂的而且还是比较核心的.在java.io.包中提供了相关的API. java中流的概念划分 流的方向: 输 ...

  2. 金三银四跳槽季,BAT美团滴滴java面试大纲(带答案版)之一:Java基础篇

    Java基础篇: 题记:本系列文章,会尽量模拟面试现场对话情景, 用口语而非书面语 ,采用问答形式来展现.另外每一个问题都附上“延伸”,这部分内容是帮助小伙伴们更深的理解一些底层细节的补充,在面试中可 ...

  3. java基础篇---HTTP协议

    java基础篇---HTTP协议   HTTP协议一直是自己的薄弱点,也没抽太多时间去看这方面的内容,今天兴致来了就在网上搜了下关于http协议,发现有园友写了一篇非常好的博文,博文地址:(http: ...

  4. java基础篇---I/O技术(三)

    接上一篇java基础篇---I/O技术(二) Java对象的序列化和反序列化 什么叫对象的序列化和反序列化 要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象 ...

  5. Java基础篇 - 强引用、弱引用、软引用和虚引用

    Java基础篇 - 强引用.弱引用.软引用和虚引用 原创零壹技术栈 最后发布于2018-09-09 08:58:21 阅读数 4936 收藏展开前言Java执行GC判断对象是否存活有两种方式其中一种是 ...

  6. java基础篇 之 构造器内部的多态行为

    java基础篇 之 构造器内部的多态行为 ​ 我们来看下下面这段代码: public class Main { public static void main(String[] args) { new ...

  7. 小白—职场之Java基础篇

    java基础篇 java基础 目录 1.java是一种什么语言,jdk,jre,jvm三者的区别 2.java 1.5之后的三大版本 3.java跨平台及其原理 4.java 语言的特点 5.什么是字 ...

  8. java基础篇1

    JAVA基础篇1 注释 单行注释 //这是一个单行注释,由两个斜杠组成,不能嵌套多行注释 多行注释 /*这是一个 多行注释 ,//里面不能嵌套多行注释, 但是可以嵌套单行注释*/ 文档注释 /**ja ...

  9. Java基础篇(JVM)——类加载机制

    这是Java基础篇(JVM)的第二篇文章,紧接着上一篇字节码详解,这篇我们来详解Java的类加载机制,也就是如何把字节码代表的类信息加载进入内存中. 我们知道,不管是根据类新建对象,还是直接使用类变量 ...

  10. Java基础篇(JVM)——字节码详解

    这是Java基础篇(JVM)的第一篇文章,本来想先说说Java类加载机制的,后来想想,JVM的作用是加载编译器编译好的字节码,并解释成机器码,那么首先应该了解字节码,然后再谈加载字节码的类加载机制似乎 ...

随机推荐

  1. SpringBoot(五) - Java8 新特性

    1.Lambda表达式 Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递).使用它可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风 ...

  2. AgileBoot - 项目内统一的错误码设计

    本篇文章主要探讨关于统一错误码的设计,并提供笔者的实现 欢迎大家讨论,指正. 该错误码的设计在仓库: github:https://github.com/valarchie/AgileBoot-Bac ...

  3. 如何用Virtualbox搭建一个虚拟机

    序言 各位好啊,我是会编程的蜗牛,作为java开发者,我们肯定会接触Linux服务器,除了使用云服务搭建Linux服务器外,我们一般也可以在自己的电脑上安装虚拟机来搭建Linux服务器用于各种功能的验 ...

  4. Droplet——一款轻量的Golang应用层框架

    Github地址 如标题所描述的,Droplet 是一个 轻量 的 中间层框架,何为中间层呢? 通常来说,我们的程序(注意这里我们仅仅讨论程序的范围,而非作为一个系统,因此这里不设计如 LB.Gate ...

  5. 长文梳理muduo网络库核心代码、剖析优秀编程细节

    前言 muduo库是陈硕个人开发的tcp网络编程库,支持Reactor模型,推荐大家阅读陈硕写的<Linux多线程服务端编程:使用muduo C++网络库>.本人前段时间出于个人学习.找工 ...

  6. Pthread 并发编程(一)——深入剖析线程基本元素和状态

    Pthread 并发编程(一)--深入剖析线程基本元素和状态 前言 在本篇文章当中讲主要给大家介绍 pthread 并发编程当中关于线程的基础概念,并且深入剖析进程的相关属性和设置,以及线程在内存当中 ...

  7. PyCharm配置远程Docker环境

    1. docker 配置 使用-p参数暴露一个端口用于ssh连接. docker run -itd --name wangchao_paddle --gpus all -p 8899:8888 -p ...

  8. Archlinux + Dwm 配置流程

    本着学习C的态度来了解dwm,本身作为一个i3wm的追崇者,与dwm会擦出怎么样的火花呢? 下载安装dwm archlinuxcn源配置 编辑/etc/pacman.conf文件,添加bfsu的arc ...

  9. Redisson源码解读-公平锁

    前言 我在上一篇文章聊了Redisson的可重入锁,这次继续来聊聊Redisson的公平锁.下面是官方原话: 它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程.所有请 ...

  10. Git配置和使用?Git你真的会用么?Git与SVN的主要区别

    1.Git环境配置    在学习Git之前,首先要知道什么是版本控制 1.1 版本控制:版本迭代.新的版本!版本管理器 版本控制是开发过程中用于管理我们的文件.目录或工程内容的修改内容,查看修改历史记 ...