文章目录
- 1JUC
- 2回顾多线程知识
- 3Lock锁(重点)
- 4Lock锁
- 5JUC版的生产者与消费者问题(虚假唤醒)
- 6有序线程
- 7八锁问题
- 8CopyOnWriteArrayList(读写复制)
- 9Set并发问题解决方案
- 10HashMap不安全解决方案
- 11线程计数器
- 13读共享锁(写独占锁)(MyCache)
- 14(BlockingQueue四组API)
- 15同步队列(SynchronousQueue、不存储元素)
- 16线程池(ThreadPoolExecutor)
- 17四大函数式接口
- 18链式编程(Stream流式计算)
- 19ForkJoin
- 20异步回调(Future)
- 21理解JMM(Volatile)
- 22Volatile可见性及非原子性验证
- 23指令重排详解
- 24单例模式(饿汉模式和懒汉模式)
- 25深入理解CAS
- 26原子引用解决ABA问题(需要引入原子引用-27节提到)
- 27可重入锁
- 28自旋锁
- 29死锁排查(jps)
- 30乐观锁、悲观锁(无内容)
1JUC
2回顾多线程知识
进程和线程:
进程:一个进程可以包含多个线程,至少是一个线程,java默认2个线程——main、GC(垃圾回收)
线程:开了一个软件,写字的时候也会有自动保存的线程在进行,Thread、Runnable、Callable
java开启不了线程,java操作的是底层的C++,java无法操作,用的是虚拟机
并发与并行:
并发:多线程操作同一个资源(假设cpu只有一核的时候,模拟多线程,快速交替处理线程)
并行:CPU多核情况下才有,多个线程同时执行
并发编程的本质:充分利用电脑的CPU
线程有几种状态:6种:new(创建)、runnable(运行)、blocked(阻塞)、wait(等待)、timed_waiting(超时等待)
terminated(终止)
wait与sleep的区别:
1.来自不同的类——wait(Object)、sleep(Thread)
2.关于锁——wait会释放锁,而sleep不会释放
3.适用范围不一样——wait必须在同步代码块中执行,sleep则不用
4.w捕获异常——wait不需要捕获异常、sleep需要捕获异常
3Lock锁(重点)
传统Synchronized解决有顺序卖票问题
package JUC;
/*基本的卖票的例子
线程是一个单独的资源类
* */
public class Demo02SaleTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"C").start();
}
}
class Ticket{
private int num=30;
public synchronized void sale(){
if (num>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"票,剩余"+num);
}
}
}
4Lock锁
package JUC;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo03Lock {
public static void main(String[] args) {
Ticket1 ticket1 = new Ticket1();
}
}
//Lock锁————三步骤——1.new ReentrantLock 2.加锁 3.解锁
class Ticket1{
private int num=30;
//ReentrantLock---java默认非公平锁,也就是默认可以插队(比较合理,3秒的不需要等待3小时的线程)
Lock lock=new ReentrantLock();
public void sale(){
lock.lock();//加锁
try {
//业务代码
if (num>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"票,剩余"+num);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();//解锁
}
}
}
5JUC版的生产者与消费者问题(虚假唤醒)
package JUC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*JUC版的生产者与消费者问题
通过Lock可以找到Condition
以下的是无序的随机的状态结果,JUC的优势还没有体现出来
ReentrantLock是可重入的意思
* */
public class Demo05JUC1 {
public static void main(String[] args) {
Date1 date1 = new Date1();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
date1.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
date1.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
date1.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
date1.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Date1{
private int num=0;
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (num!=0){
//等待
//this.wait();JUC之前普通的
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+"=="+num);
//通知线程B,我+1完毕了this.notifyAll();JUC之前普通的
condition.signalAll();//唤醒全部
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (num==0){
//等待
//this.wait();
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"=="+num);
//通知线程B,我-1完毕了 JUC之前的this.notifyAll();
condition.signalAll();//唤醒全部
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
6有序线程
package JUC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*实现生产者与消费者的有序JUC执行
三条线程按照循环顺序执行
* */
public class Demo06JUC2 {
public static void main(String[] args) {
new Thread(()->{
},"A").start();
new Thread(()->{
},"B").start();
new Thread(()->{
},"C").start();
}
}
//资源类
class Date3{
private Lock lock=new ReentrantLock();
private Condition condition = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int num=1;
public void print1(){
lock.lock();
try {
//业务代码,判断-执行-通知
if (num!=1){
condition.await();
}
System.out.println(Thread.currentThread().getName()+"1111");
//通知唤醒
num=2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print2(){
lock.lock();
try {
if (num!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"2222");
num=3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print3(){
lock.lock();
try {
if (num!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"3333");
num=1;
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
7八锁问题
package JUC;
import java.util.concurrent.TimeUnit;
/*什么是锁,锁的对象是谁——查看自己的收藏笔记
8锁,就是关于锁的8个问题
1.标准情况下,线程先执行哪个?————先发短信再打电话(原因:锁的存在)
2.发短信延迟4秒的情况下————还是先短信再电话,先执行发短信,因为有同步锁
3.增加了一个普通方法后,是执行发短信还是hello,限制性普通方法,普通方法不受锁的限制
4.两个对象的情况下,先执行发短信还是打电话——————打电话,因为两把锁不一样,打电话的锁有延迟
5.静态方法的时候,两个线程先执行哪个?——————加了static以后锁的就是class对象,类的模板只有一个所以和原先锁的是对象没有关系
* */
public class Demo07suo1 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
//第一条线程
new Thread(()->{
phone.send();
},"A").start(