juc
编程
1、进程和线程
1.1、什么是进程
进程是正在运行的程序的实例
1.2、传承的synchronized锁
package com.baidu;
public class ThickUp {
public static void main(String[] args) {
MakeUp makeUp = new MakeUp(0, "x");
MakeUp makeUp2 = new MakeUp(2, "y");
makeUp.start();
makeUp2.start();
}
}
class Lipestick {
}
class Mirror {
}
class MakeUp extends Thread {
// 只有一份
static Lipestick lipestick = new Lipestick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
public MakeUp(int choice, String giString) {
this.choice = choice;
this.girlName = girlName;
}
public void run() {
try {
makeup();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipestick) {
System.out.println(this.getName() + "获得口红的锁");
Thread.sleep(1000);
synchronized (mirror) {
System.out.println(this.getName() + "获得镜子的锁");
}
}
} else {
synchronized (mirror) {
System.out.println(this.getName() + "获得镜子的锁");
Thread.sleep(2000);
synchronized (lipestick) {
System.out.println(this.getName() + "获得口红的锁");
}
}
}
}
}
synchronized锁,当一个方法拿到锁的时候其他线程,会的等待拿到锁的类结束,才会执行
注意:如果两个对象拿着锁程序就会卡住
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WrLtjXv6-1614251489453)(C:\Users\ameng\Pictures\Camera Roll\2615789-08f16aeac7e0977d.webp)]
1.3、notifyAll
和await
方法
await 使线程休眠
notifyAll
唤醒休眠的线程
package com.baidu;
public class FlagPC2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
class Player extends Thread{
TV tv;
public Player(TV tv)
{
this.tv=tv;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
if(i%2==0)
{
this.tv.play("快乐大本营");
}
else
{
this.tv.play("抖音");
}
}
}
}
class Watcher extends Thread
{
TV tv;
public Watcher(TV tv)
{
this.tv=tv;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
tv.waich();
}
}
}
class TV{
String voice;
boolean flag=true;
public synchronized void play(String voice)
{
if(!flag)
{
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("演员表演了:"+voice);
this.notifyAll();
this.voice=voice;
this.flag=!this.flag;
}
public synchronized void waich()
{
if(flag)
{
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("我观看了"+voice);
this.notifyAll();
this.flag=!this.flag;
}
}
1.4、lock锁
/*
* 使用ReentrantLock类实现同步
* */
class MyReenrantLock implements Runnable{
//继承接口
private Lock lock = new ReentrantLock();
public void run() {
//上锁
lock.lock();
for(int i = 0; i < 5; i++) {
System.out.println("当前线程名: "+ Thread.currentThread().getName()+" ,i = "+i);
}
//释放锁
lock.unlock();
}
}
public class MyLock {
public static void main(String[] args) {
MyReenrantLock myReenrantLock = new MyReenrantLock();
Thread thread1 = new Thread(myReenrantLock);
Thread thread2 = new Thread(myReenrantLock);
Thread thread3 = new Thread(myReenrantLock);
thread1.start();
thread2.start();
thread3.start();
}
lock锁使用的步骤:
1.实列化lock锁
private Lock lock = new ReentrantLock();
2.上锁
lock.lock();
3.解锁
lock.unlock();
1.5、condition实现唤醒和休眠
1.实列condition类
lock.newCondition()
2.唤醒线程方法
signalAll();
3.休眠方法
await
2、多线程集合
2.1、集合在多线程是否安全
集合在多线程中大部分都是不安全
2.2、collections类
collections是集合中辅助类可以使用以下的方法来使集合变得安全
Collections.synchronizedList(List);
Collections.synchronizedSet(Set);
Collections.synchronizedMap(Map);
2.3、三大安全集合类
三大使线路安全的集合类
CopyOnWriteArrayList
安全的list集合类
CopyOnWriteArraySet
安全的set集合类
concurrentHashMap
安全的map集合类
3、Callable
3.1、初始Callable
public class Call {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// TODO Auto-generated method stub
Thread1 thread = new Thread1();
FutureTask futureTask = new FutureTask(thread);
new Thread(futureTask,"A").start();
futureTask.get();
}
}
class Thread1 implements Callable<Integer>
{
@Override
public Integer call() {
System.out.println("hello");
return 1;
}
}
Callable
需要用其他类的FutureTask Callable是有返回值的的接口
3.2、CounDownLatch
计数器CounDownLatch
使控制线程
``countDownLatch.await()` 等待方法
countDownLatch.countDown();
实现计数器-1
public class CountDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <=6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"go");
countDownLatch.countDown();
}
,String.valueOf(i)
).start();
}
countDownLatch.await();
System.out.println("end");
}
}
3.3、cyclicBarrier
public class Cyclic {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙");
});
for (int i = 1; i <= 7; i++) {
final int temp = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "->第" + temp + "课龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
cyclicBarrier 如果线程到了cyclicBarrier的的方法才运行
3.4、semaphore
public class Semp {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
final int temp=i;
new Thread(
()->{
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"得到"+temp);
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName()+"失去"+temp);
semaphore.release();
}
}
).start();
}
}
semaphore类相当于一个停车库,只有等待先到的线程结束,
其他线程才能进入
3.5、ReadWriteLock
ReadWriteLock
使用步骤
实列 ReentrantReadWriteLock
写锁 readLock
读锁 writeLock
4、堵塞队列
4.1、什么是是堵塞队列
- 当阻塞队列是空时,从队列中获取元素的操作将会被阻塞
- 当阻塞队列是满时,从队列中添加元素的操作将会被阻塞
4.2、堵塞常用方法
抛出异常 | 返回值 | 阻塞 | 超时 | |
---|---|---|---|---|
插入 | add(Object obj) | offer(Objject obj) | put(Object obj) | offer(Object obj,int time,unit) |
移除 | remove() | pull() | take() | pull(time,unit) |
检查 | element() | peek() | 不可用 | 不可用 |
4.3、synchronousQueue
synchronousQueue
同步队列
5、线程池
5.1、初始线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。
5.2、三大方法、7大参数、四大拒绝参数
public class ThreadPool {
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,// 核心线程数
5, //最大数
3, //最大时间数
TimeUnit.SECONDS,//时间单位
new LinkedBlockingDeque<>(3),//等待数
Executors.defaultThreadFactory(),//工厂模式
new ThreadPoolExecutor.CallerRunsPolicy());//拒绝方针
for (int i = 0; i < 100; i++) {
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName()+"ok");
});
}
threadPoolExecutor.shutdown();
}
}
1.三大方法
Executors.newFixedThreadPool()
国定线程数
Executors.newSingleThreadExecutor()
单列线程
Executors.newCachedThreadPool()
来多少线程就有多少线程
2.7、大参数、
核心线程数
最大数
最大时间数
时间单位
等待数
工厂模式
拒绝方针
3、四大拒绝策略
AbortPolicy:为线程池默认的拒绝策略,该策略直接抛异常处理。
DiscardPolicy:直接抛弃不处理。
DiscardOldestPolicy:丢弃队列中最老的任务。
CallerRunsPolicy:将任务分配给当前执行execute方法线程来处理。
5.3、最大线程如何定义
cpu
密集型 根据cpu
定义最大线程
IO 密集型 》判断你程序十分占IO流的线程
6、四大函数接口
6.1、函数接口和断定型接口
函数接口:只有一个方法叫函数接口
public class Demo01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// Function<String, String> function = new Function<String,String>(){
// public String apply(String strs)
// {
// return strs;
// }
// };
Function<String, String> function =(st)->{
return st;
};
}}
断定型接口:有转入参数只能为真假值
public class Demo02 {
public static void main(String[] args) {
//有转入参数只能为真假值 断定型接口
new Predicate<String>() {
public boolean test(String str)
{return false;}
};
}
}
6.2、消费型接口和供给接口
消费型接口:只有输入没有返回值
foreach()
消费者类的函数型接口
public class Demo03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//只有输入没有返回值 消费型接口
new Consumer<String>() {
public void accept(String t) {
// TODO Auto-generated method stub
}
};
}
}
供给接口:只有参数没有返回值
public class Demo04 {
//供给型接口 只有参数没有返回值
public static void main(String[] args) {
new Supplier<Integer>() {
@Override
public Integer get() {
// TODO Auto-generated method stub
return null;
}
};
}
}
6.3、Stream流式计算
public class Demo01 {
public static void main(String[] args) {
User u1 = new User(1, "a", 15);
User u2 = new User(4, "b", 15);
User u3 = new User(3, "c", 19);
User u4 = new User(5, "d", 22);
User u5 = new User(7, "e", 21);
List<User> list = Arrays.asList(u1,u2,u3,u4,u5);
list.stream()
.filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>13;})
.map(u->{return u.getName().toUpperCase();})
.limit(1)
.forEach(System.out::println);
;
}
}
6.4、ForkJoin
使用Fork,Join
来进行并归计算
6.5、异步计算
Future模式是多线程开发中非常常见的一种设计模式。它的核心思想是异步调用。当我们需要调用一个函数方法时。如果这个函数执行很慢,那么我们就要进行等待。但有时候,我们可能并不急着要结果。因此,我们可以让被调用者立即返回,让他在后台慢慢处理这个请求。
public class Demo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
// System.out.println("hello");
// }
// );
// System.out.println("jie");
// future.get();
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()->{
System.out.println("asdkj");
return 1024;
});
future.whenComplete((u,t)->{
System.out.println("n"+t);
System.out.println("z"+u);
}).exceptionally((e)->{
System.out.println("error");
return 233;
}).get();
System.out.println("jie");
}
}
7、jmm
7.1、什么是jmm
JMM
即是JAVA内存模型,是个概念,没有物理地址
7.2、jmm
的操作
主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完成:
- lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
- unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
- read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
- write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
Java内存模型还规定了在执行上述八种基本操作时,必须满足如下规则:
- 如果要把一个变量从主内存中复制到工作内存,就需要按顺寻地执行read和load操作, 如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。但Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。
- 不允许read和load、store和write操作之一单独出现
- 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。
- 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
- 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
- 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现
- 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值
- 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
- 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。
7.3、volatile关键字
volatile的三个特点
-
保证线程的可见性
可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。
-
不保证原子性
原子是世界上的最小单位,具有不可分割性
-
禁止指令重排
Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。
我们都知道,为了性能优化,
JMM
在不改变正确语义的前提下,会允许编译器和处理器对指令序列进行重排序,那如果想阻止重排序要怎么办了?答案是可以添加内存屏障。
8、CAS
8.1、什么是CAS
机制
CAS
是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
CAS
机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
// compareanset:比较并交换
//期望、更新 如果我期望的达到了 就更新 否则就不更新,cas 是cup的并发原理
atomicInteger.compareAndSet(2020, 2021);
System.out.println(atomicInteger.get());
8.2、解决ABA
(乐观锁)
可以通过原子性来解决乐观锁问题
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(2020, 0);
//new AtomicReference<>()
atomicStampedReference.compareAndSet(null, null, 0, 0);
9、锁
9.1、可重入锁
锁里带锁
9.2、自旋锁
自旋锁是专为防止多处理器并发而引入的一种锁,它在内核大量应用于中断处理中断等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,即在标志寄存器中关闭/打开中断标志位,不需要自旋锁)。
9.3、怎么排除死锁
日志
堆栈