共计 4777 个字符,预计需要花费 12 分钟才能阅读完成。
什么是 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、分布式缓存、数据结构等等。