该学习笔记是本人依据相关的学习视频整体汇总,相关的视频学习可以自己去搜看看。
【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili
一、什么是JUC
从中就可以看出JUC,实质就是三个包,后面晖详细说明三个包下各个类功能。
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
二、线程和进程
1.区别
进程:就是一个程序,比如QQ、音乐、微信;
线程:一个进程可以包含多个线程,至少包含一个线程;
java默认连个线程:main线程+GC线程;
线程实现方式:Thread、Runnable、Callable
2.JAVA实质无法开启线程,而是由底层C++进行开启的,开启的线程源码如下:
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()方法如下:
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 */
}
}
}
// 其实调用的就是本地的线程开启方法
private native void start0();
3.线程的6种状态:来源于Threa种的源码查看
public enum State {
// 创建
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待,死等
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
4.wait和sleep区别
1)来自类不同:wait->Object;sleep-> Thread
2) 锁的释放:wait->释放锁,sleep-> 不释放锁
3)使用范围:wait-> 同步代码块种,sleep-> 随时
4)是否需要捕获异常:wait->不需要捕获异常,sleep->需要捕获异常
【补充知识】:常用睡眠的方法,TimeUnit来自java.util.concurrent包 TimeUnit.SECONDS.sleep(2); // 秒 TimeUnit.DAYS.sleep(2); // 天 TimeUnit.HOURS.sleep(2); // 小时
5.并发和并行
1、并发(多个线程抢一个资源)
CPU一核,模拟出多个线程,快速叠替
2、并行(多个人并排行走)
CPU多核,多个线程可以同时执行,线程池;
3、并发本质
充分利用CPU资源
【补充知识】:查看服务器的核数
Runtime.getRuntime().availableProcessors();
三、Lock锁(重点)
1.传统的synchronized
package com.project.concurrency.synchronizedStudy_1;
/**
* 售票功能
*/
public class SynchronizedSaleDemo {
public static void main(String[] args) {
Tack dataA = new Tack();
new Thread(()->{
for (int i = 0; i < 20; i++) {
dataA.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
dataA.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
dataA.sale();
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
dataA.sale();
}
},"D").start();
}
}
class Tack{
private int number = 60;
protected synchronized void sale(){
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖了第"+(number--)+"张票,还剩"+number+"张票");
}else {
System.out.println(Thread.currentThread().getName()+"没有买到票");
}
}
}
2.Lock锁使用方式
从JDK种可以查看到对应Lock使用的一些简单描述:
Lock是一个接口,有三个实现类,分别是如下:
-
ReentrantLock 可重入锁 (常用)
-
ReentrantReadWriteLock.ReadLock 读锁
-
ReentrantReadWriteLock.WriteLock 写锁
1)ReentrantLock 可重入锁,有两个构造方法:
// 创建非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 根据参数进行选择创建非公平锁还是公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
【公平锁】很公平,进行先来后到
【非公平锁】不公平,可以插队,依据cpu选择执行前后(默认)
2)ReentrantLock 可重入锁的使用依据举例
使用方式如下:
// 创建锁
Lock l = ...;
// 加锁
l.lock();
try {
// 执行业务代码
} finally {
// 释放锁
l.unlock();
}
举例:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 采用Lock 实现买票
*/
public class LockDemo {
public static void main(String[] args) {
DataA dataA = new DataA();
new Thread(()->{
for (int i = 0; i < 10; i++) {
dataA.increamNumber();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
dataA.decrement();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
dataA.increamNumberC();
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
dataA.decrementD();
}
},"D").start();
}
}
class DataA{
private int number = 0;
Lock lock = new ReentrantLock();
public void increamNumber() {
// 加锁
lock.lock();
try {
if(number>0){
number--;
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 解锁
lock.unlock();
}
}
}
3)synchronized与Lock区别
1.synchronized是关键字,Lock是类接口;
2.synchronized无法获取锁的状态,Lock可以查看所得状态;
3.synchronized会自动释放锁,Lock必须手动释放锁,否则会死锁;
4.synchronized(线程一,阻塞,线程二就会死死的等),Lock就不一定等了;
5.synchronized可重入锁,不可中断,非公平的,Lock 可重入锁,可中断, 可选公平或是非公平;
6.synchronized适合锁少量代码,Lock适合大量代码同步问题。
【可重入锁】可重入是指一个线程如果获取了锁,那么它就是锁的主人,那么它可以再次获取这把锁,这种就是理解为重入,简而言之,可以重复获取同一把锁,不会造成阻塞死锁。例如:
@Test
public void testRepeatLock() {
ReentrantLock reentrantLock = new ReentrantLock();
// 第一次获取锁
reentrantLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " first get lock");
// 再次获取锁
tryAgainLock(reentrantLock);
}finally {
reentrantLock.unlock();
}
}
public void tryAgainLock(ReentrantLock reentrantLock) {
// 第2次获取锁
reentrantLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " second get lock");
}finally {
reentrantLock.unlock();
}
}
四、生产者消费者问题实现
需求:实现线程一:变量+1,线程二:变量-1,两个线程交替工作
package com.project.concurrency.lockStudy_2;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 采用Lock 实现生产者、消费者
*/
public class LockDemo {
public static void main(String[] args) {
DataA dataA = new DataA();
new Thread(()->{
for (int i = 0; i < 10; i++) {
dataA.increamNumber();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
dataA.decrement();
}
},"B").start();
}
}
class DataA{
private int number = 0;
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
// +1,采用while的就是避免唤醒之后没有判断就立马执行下一行
public void increamNumber() {
// 加锁
lock.lock();
try {
while (number!=0){
// 等待
conditionA.await();
}
number = number+1;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 唤醒conditionB,也可以唤醒全部,condition.signalAll
conditionB.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
// 解锁
lock.unlock();
}
}
// -1
public void decrement() {
lock.lock();
try {
while (number!=1){
// 等待
conditionB.await();
}
number = number-1;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 唤醒某个线程
conditionA.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
// 解锁
lock.unlock();
}
}
}
五、锁是什么,锁的对象是什么?8锁问题
1.synchronized修饰普通方法:锁的是方法的调用者(对象);
2.synchronized修饰静态方法:锁的是方法归属的类(Class);
3.synchronized修饰代码快synchronized(this),锁得是对象;