1.java中叫锁,操作系统称为monitor(监视器)
2.虚假唤醒问题需求:对数字0交替执行+1和-1操作
备注:synchronized写法中wait()是等待,notify()是唤醒。lock写法中await()是等待,signal()是唤醒。
有问题的代码:
package com.atguigu.sync;
//第一步 创建资源类,定义属性和操作方法
class Share {
//初始值
private int number = 0;
//+1的方法
public synchronized void incr() throws InterruptedException {
//第二步 判断 干活 通知
if(number != 0) { //判断number值是否是0,如果不是0,等待
this.wait(); //在哪里睡,就在哪里醒
}
//如果number值是0,就+1操作
number++;
System.out.println(Thread.currentThread().getName()+" :: "+number);
//通知其他线程
this.notifyAll();
}
//-1的方法
public synchronized void decr() throws InterruptedException {
//判断
if(number != 1) {
this.wait();
}
//干活
number--;
System.out.println(Thread.currentThread().getName()+" :: "+number);
//通知其他线程
this.notifyAll();
}
}
public class ThreadDemo1 {
//第三步 创建多个线程,调用资源类的操作方法
public static void main(String[] args) {
Share share = new Share();
//创建线程
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"DD").start();
}
}
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
DD :: 0
BB :: -1
CC :: 0
CC :: 1
BB :: 0
DD :: -1
AA :: 0
AA :: 1
DD :: 0
CC :: 1
DD :: 0
AA :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0Process finished with exit code 0
解释:
wait()方法会释放锁。
new ReentrantLock(); ---- 可重入锁
当AA抢到线程后会+1,此时为1。之后唤醒其他线程,若此时CC抢到,由于CC判断if(number!=1),CC线程进行等待,同时会释放锁。若此时AA抢到,则AA在if判断处也进行等待,同时会释放锁。若CC又抢到,即CC被唤醒,由于wait()是在哪里睡在哪里醒,此时被唤醒后,CC线程在wait()方法后执行,就执行了+1,此时为2。这就是虚假唤醒,出问题了。此处为出问题的其中一种情况,其他情况会出现-1等负数出现的情况。
解决方案:
if关键词改为while关键词。wait()是在哪里睡就在哪里醒,醒来后if就不生效了,while会使无论何时行,while都会执行。
代码如下1:
package com.atguigu.sync;
//第一步 创建资源类,定义属性和操作方法
class Share {
//初始值
private int number = 0;
//+1的方法
public synchronized void incr() throws InterruptedException {
//第二步 判断 干活 通知
while (number != 0) { //判断number值是否是0,如果不是0,等待
this.wait(); //在哪里睡,就在哪里醒
}
//如果number值是0,就+1操作
number++;
System.out.println(Thread.currentThread().getName()+" :: "+number);
//通知其他线程
this.notifyAll();
}
//-1的方法
public synchronized void decr() throws InterruptedException {
//判断
while (number != 1) {
this.wait();
}
//干活
number--;
System.out.println(Thread.currentThread().getName()+" :: "+number);
//通知其他线程
this.notifyAll();
}
}
public class ThreadDemo1 {
//第三步 创建多个线程,调用资源类的操作方法
public static void main(String[] args) {
Share share = new Share();
//创建线程
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"DD").start();
}
}
代码如下2(另一种正确写法:不用synchronized,用lock):
package com.atguigu.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//第一步 创建资源类,定义属性和操作方法
class Share {
private int number = 0;
//创建Lock
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//+1
public void incr() throws InterruptedException {
//上锁
lock.lock();
try {
//判断
while (number != 0) {
condition.await();
}
//干活
number++;
System.out.println(Thread.currentThread().getName()+" :: "+number);
//通知
condition.signalAll();
}finally {
//解锁
lock.unlock();
}
}
//-1
public void decr() throws InterruptedException {
lock.lock();
try {
while(number != 1) {
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+" :: "+number);
condition.signalAll();
}finally {
lock.unlock();
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
Share share = new Share();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"DD").start();
}
}
打印如下:
AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0Process finished with exit code 0
升级:定制化通信,指定线程运行顺序
package com.atguigu.lock;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//第一步 创建资源类
class ShareResource {
//定义标志位
private int flag = 1; // 1 AA 2 BB 3 CC
//创建Lock锁
private Lock lock = new ReentrantLock();
//创建三个condition
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
//打印5次,参数第几轮
public void print5(int loop) throws InterruptedException {
//上锁
lock.lock();
try {
//判断
while(flag != 1) {
//等待
c1.await();
}
//干活
for (int i = 1; i <=5; i++) {
System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
}
//通知
flag = 2; //修改标志位 2
c2.signal(); //通知BB线程
}finally {
//释放锁
lock.unlock();
}
}
//打印10次,参数第几轮
public void print10(int loop) throws InterruptedException {
lock.lock();
try {
while(flag != 2) {
c2.await();
}
for (int i = 1; i <=10; i++) {
System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
}
//修改标志位
flag = 3;
//通知CC线程
c3.signal();
}finally {
lock.unlock();
}
}
//打印15次,参数第几轮
public void print15(int loop) throws InterruptedException {
lock.lock();
try {
while(flag != 3) {
c3.await();
}
for (int i = 1; i <=15; i++) {
System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
}
//修改标志位
flag = 1;
//通知AA线程
c1.signal();
}finally {
lock.unlock();
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
shareResource.print5(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
shareResource.print10(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
shareResource.print15(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
}
}
3.集合线程安全
AA :: 1 :轮数:1
AA :: 2 :轮数:1
AA :: 3 :轮数:1
AA :: 4 :轮数:1
AA :: 5 :轮数:1
BB :: 1 :轮数:1
BB :: 2 :轮数:1
BB :: 3 :轮数:1
BB :: 4 :轮数:1
BB :: 5 :轮数:1
BB :: 6 :轮数:1
BB :: 7 :轮数:1
BB :: 8 :轮数:1
BB :: 9 :轮数:1
BB :: 10 :轮数:1
CC :: 1 :轮数:1
CC :: 2 :轮数:1
CC :: 3 :轮数:1
CC :: 4 :轮数:1
CC :: 5 :轮数:1
CC :: 6 :轮数:1
CC :: 7 :轮数:1
CC :: 8 :轮数:1
CC :: 9 :轮数:1
CC :: 10 :轮数:1
CC :: 11 :轮数:1
CC :: 12 :轮数:1
CC :: 13 :轮数:1
CC :: 14 :轮数:1
CC :: 15 :轮数:1
AA :: 1 :轮数:2
AA :: 2 :轮数:2
AA :: 3 :轮数:2
AA :: 4 :轮数:2
AA :: 5 :轮数:2
BB :: 1 :轮数:2
BB :: 2 :轮数:2
BB :: 3 :轮数:2
BB :: 4 :轮数:2
BB :: 5 :轮数:2
BB :: 6 :轮数:2
BB :: 7 :轮数:2
BB :: 8 :轮数:2
BB :: 9 :轮数:2
BB :: 10 :轮数:2
CC :: 1 :轮数:2
CC :: 2 :轮数:2
CC :: 3 :轮数:2
CC :: 4 :轮数:2
CC :: 5 :轮数:2
CC :: 6 :轮数:2
CC :: 7 :轮数:2
CC :: 8 :轮数:2
CC :: 9 :轮数:2
CC :: 10 :轮数:2
CC :: 11 :轮数:2
CC :: 12 :轮数:2
CC :: 13 :轮数:2
CC :: 14 :轮数:2
CC :: 15 :轮数:2
AA :: 1 :轮数:3
AA :: 2 :轮数:3
AA :: 3 :轮数:3
AA :: 4 :轮数:3
AA :: 5 :轮数:3
BB :: 1 :轮数:3
BB :: 2 :轮数:3
BB :: 3 :轮数:3
BB :: 4 :轮数:3
BB :: 5 :轮数:3
BB :: 6 :轮数:3
BB :: 7 :轮数:3
BB :: 8 :轮数:3
BB :: 9 :轮数:3
BB :: 10 :轮数:3
CC :: 1 :轮数:3
CC :: 2 :轮数:3
CC :: 3 :轮数:3
CC :: 4 :轮数:3
CC :: 5 :轮数:3
CC :: 6 :轮数:3
CC :: 7 :轮数:3
CC :: 8 :轮数:3
CC :: 9 :轮数:3
CC :: 10 :轮数:3
CC :: 11 :轮数:3
CC :: 12 :轮数:3
CC :: 13 :轮数:3
CC :: 14 :轮数:3
CC :: 15 :轮数:3
AA :: 1 :轮数:4
AA :: 2 :轮数:4
AA :: 3 :轮数:4
AA :: 4 :轮数:4
AA :: 5 :轮数:4
BB :: 1 :轮数:4
BB :: 2 :轮数:4
BB :: 3 :轮数:4
BB :: 4 :轮数:4
BB :: 5 :轮数:4
BB :: 6 :轮数:4
BB :: 7 :轮数:4
BB :: 8 :轮数:4
BB :: 9 :轮数:4
BB :: 10 :轮数:4
CC :: 1 :轮数:4
CC :: 2 :轮数:4
CC :: 3 :轮数:4
CC :: 4 :轮数:4
CC :: 5 :轮数:4
CC :: 6 :轮数:4
CC :: 7 :轮数:4
CC :: 8 :轮数:4
CC :: 9 :轮数:4
CC :: 10 :轮数:4
CC :: 11 :轮数:4
CC :: 12 :轮数:4
CC :: 13 :轮数:4
CC :: 14 :轮数:4
CC :: 15 :轮数:4
AA :: 1 :轮数:5
AA :: 2 :轮数:5
AA :: 3 :轮数:5
AA :: 4 :轮数:5
AA :: 5 :轮数:5
BB :: 1 :轮数:5
BB :: 2 :轮数:5
BB :: 3 :轮数:5
BB :: 4 :轮数:5
BB :: 5 :轮数:5
BB :: 6 :轮数:5
BB :: 7 :轮数:5
BB :: 8 :轮数:5
BB :: 9 :轮数:5
BB :: 10 :轮数:5
CC :: 1 :轮数:5
CC :: 2 :轮数:5
CC :: 3 :轮数:5
CC :: 4 :轮数:5
CC :: 5 :轮数:5
CC :: 6 :轮数:5
CC :: 7 :轮数:5
CC :: 8 :轮数:5
CC :: 9 :轮数:5
CC :: 10 :轮数:5
CC :: 11 :轮数:5
CC :: 12 :轮数:5
CC :: 13 :轮数:5
CC :: 14 :轮数:5
CC :: 15 :轮数:5
AA :: 1 :轮数:6
AA :: 2 :轮数:6
AA :: 3 :轮数:6
AA :: 4 :轮数:6
AA :: 5 :轮数:6
BB :: 1 :轮数:6
BB :: 2 :轮数:6
BB :: 3 :轮数:6
BB :: 4 :轮数:6
BB :: 5 :轮数:6
BB :: 6 :轮数:6
BB :: 7 :轮数:6
BB :: 8 :轮数:6
BB :: 9 :轮数:6
BB :: 10 :轮数:6
CC :: 1 :轮数:6
CC :: 2 :轮数:6
CC :: 3 :轮数:6
CC :: 4 :轮数:6
CC :: 5 :轮数:6
CC :: 6 :轮数:6
CC :: 7 :轮数:6
CC :: 8 :轮数:6
CC :: 9 :轮数:6
CC :: 10 :轮数:6
CC :: 11 :轮数:6
CC :: 12 :轮数:6
CC :: 13 :轮数:6
CC :: 14 :轮数:6
CC :: 15 :轮数:6
AA :: 1 :轮数:7
AA :: 2 :轮数:7
AA :: 3 :轮数:7
AA :: 4 :轮数:7
AA :: 5 :轮数:7
BB :: 1 :轮数:7
BB :: 2 :轮数:7
BB :: 3 :轮数:7
BB :: 4 :轮数:7
BB :: 5 :轮数:7
BB :: 6 :轮数:7
BB :: 7 :轮数:7
BB :: 8 :轮数:7
BB :: 9 :轮数:7
BB :: 10 :轮数:7
CC :: 1 :轮数:7
CC :: 2 :轮数:7
CC :: 3 :轮数:7
CC :: 4 :轮数:7
CC :: 5 :轮数:7
CC :: 6 :轮数:7
CC :: 7 :轮数:7
CC :: 8 :轮数:7
CC :: 9 :轮数:7
CC :: 10 :轮数:7
CC :: 11 :轮数:7
CC :: 12 :轮数:7
CC :: 13 :轮数:7
CC :: 14 :轮数:7
CC :: 15 :轮数:7
AA :: 1 :轮数:8
AA :: 2 :轮数:8
AA :: 3 :轮数:8
AA :: 4 :轮数:8
AA :: 5 :轮数:8
BB :: 1 :轮数:8
BB :: 2 :轮数:8
BB :: 3 :轮数:8
BB :: 4 :轮数:8
BB :: 5 :轮数:8
BB :: 6 :轮数:8
BB :: 7 :轮数:8
BB :: 8 :轮数:8
BB :: 9 :轮数:8
BB :: 10 :轮数:8
CC :: 1 :轮数:8
CC :: 2 :轮数:8
CC :: 3 :轮数:8
CC :: 4 :轮数:8
CC :: 5 :轮数:8
CC :: 6 :轮数:8
CC :: 7 :轮数:8
CC :: 8 :轮数:8
CC :: 9 :轮数:8
CC :: 10 :轮数:8
CC :: 11 :轮数:8
CC :: 12 :轮数:8
CC :: 13 :轮数:8
CC :: 14 :轮数:8
CC :: 15 :轮数:8
AA :: 1 :轮数:9
AA :: 2 :轮数:9
AA :: 3 :轮数:9
AA :: 4 :轮数:9
AA :: 5 :轮数:9
BB :: 1 :轮数:9
BB :: 2 :轮数:9
BB :: 3 :轮数:9
BB :: 4 :轮数:9
BB :: 5 :轮数:9
BB :: 6 :轮数:9
BB :: 7 :轮数:9
BB :: 8 :轮数:9
BB :: 9 :轮数:9
BB :: 10 :轮数:9
CC :: 1 :轮数:9
CC :: 2 :轮数:9
CC :: 3 :轮数:9
CC :: 4 :轮数:9
CC :: 5 :轮数:9
CC :: 6 :轮数:9
CC :: 7 :轮数:9
CC :: 8 :轮数:9
CC :: 9 :轮数:9
CC :: 10 :轮数:9
CC :: 11 :轮数:9
CC :: 12 :轮数:9
CC :: 13 :轮数:9
CC :: 14 :轮数:9
CC :: 15 :轮数:9
AA :: 1 :轮数:10
AA :: 2 :轮数:10
AA :: 3 :轮数:10
AA :: 4 :轮数:10
AA :: 5 :轮数:10
BB :: 1 :轮数:10
BB :: 2 :轮数:10
BB :: 3 :轮数:10
BB :: 4 :轮数:10
BB :: 5 :轮数:10
BB :: 6 :轮数:10
BB :: 7 :轮数:10
BB :: 8 :轮数:10
BB :: 9 :轮数:10
BB :: 10 :轮数:10
CC :: 1 :轮数:10
CC :: 2 :轮数:10
CC :: 3 :轮数:10
CC :: 4 :轮数:10
CC :: 5 :轮数:10
CC :: 6 :轮数:10
CC :: 7 :轮数:10
CC :: 8 :轮数:10
CC :: 9 :轮数:10
CC :: 10 :轮数:10
CC :: 11 :轮数:10
CC :: 12 :轮数:10
CC :: 13 :轮数:10
CC :: 14 :轮数:10
CC :: 15 :轮数:10Process finished with exit code 0
总览:
ArrayList集合不安全为题演示:
需求:实现简单的list添加数据功能。
代码如下:
package com.atguigu.self;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class Test {
public static void main(String[] args) {
// 创建ArrayList集合
List list = new ArrayList<>();
for (int i = 0; i <30; i++) {
new Thread(()->{
//向集合添加内容
list.add(UUID.randomUUID().toString().substring(0,8));
//从集合获取内容
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
出现的问题:
java.util.ConcurrentModificationException
解决方案:
代码如下1:vector方法
package com.atguigu.self;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
public class Test {
public static void main(String[] args) {
// 创建vector
List list = new Vector<>();
for (int i = 0; i <30; i++) {
new Thread(()->{
//向集合添加内容
list.add(UUID.randomUUID().toString().substring(0,8));
//从集合获取内容
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
解释:ArrayList()源码中的add()方法没有加synchronized,Vector()源码中添加了。
代码如下2:Collections工具类解决
package com.atguigu.self;
import java.util.*;
public class Test {
public static void main(String[] args) {
// Collections工具类解决
List list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i <30; i++) {
new Thread(()->{
//向集合添加内容
list.add(UUID.randomUUID().toString().substring(0,8));
//从集合获取内容
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
代码如下3:CopyOnWriteArrayList解决---JUC类中的(写时复制技术)
package com.atguigu.self;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class Test {
public static void main(String[] args) {
// JUC中的CopyOnWriteArrayList解决
List list = new CopyOnWriteArrayList<>();
for (int i = 0; i <30; i++) {
new Thread(()->{
//向集合添加内容
list.add(UUID.randomUUID().toString().substring(0,8));
//从集合获取内容
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
HaspSet和HashMap线程不安全问题:
HaspSet这种情况与ArrayList问题相同,解决方案也类似,使用CopyOnWriteArraySet<>();
HashMap这种情况也与AllayList问题相同,解决方案也类似,使用ConcurrentHashMap<>();
代码如下:
package com.atguigu.self;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class Test {
public static void main(String[] args) {
// 演示Hashset
// Set set = new HashSet<>();
Set set = new CopyOnWriteArraySet<>();
for (int i = 0; i <30; i++) {
new Thread(()->{
//向集合添加内容
set.add(UUID.randomUUID().toString().substring(0,8));
//从集合获取内容
System.out.println(set);
},String.valueOf(i)).start();
}
// 演示HashMap
// Map map = new HashMap<>();
Map map = new ConcurrentHashMap<>();
for (int i = 0; i <30; i++) {
String key = String.valueOf(i);
new Thread(()->{
//向集合添加内容
map.put(key,UUID.randomUUID().toString().substring(0,8));
//从集合获取内容
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
4.synchronized锁的八种情况
见代码路径:
C:Users惜听documents百度网盘JUC源juc_atguigusrccomatguigusyncLock_8.java
5.公平锁和非公平锁公平锁不会使一个线程运行所有。非公平锁效率高
package com.atguigu.lock;
import java.util.concurrent.locks.ReentrantLock;
//第一步 创建资源类,定义属性和和操作方法
class LTicket {
//票数量
private int number = 30;
//创建可重入锁 true为公平锁,false为非公平锁,默认为false
private final ReentrantLock lock = new ReentrantLock(true);
//卖票方法
public void sale() {
//上锁
lock.lock();
try {
//判断是否有票
if(number > 0) {
System.out.println(Thread.currentThread().getName()+" :卖出"+(number--)+" 剩余:"+number);
}
} finally {
//解锁
lock.unlock();
}
}
}
public class LSaleTicket {
//第二步 创建多个线程,调用资源类的操作方法
//创建三个线程
public static void main(String[] args) {
LTicket ticket = new LTicket();
new Thread(()-> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"AA").start();
new Thread(()-> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"BB").start();
new Thread(()-> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"CC").start();
}
}
6.可重入锁(递归锁)
lock的可重入锁实现
package com.atguigu.self;
import com.atguigu.sync.SyncLockDemo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
public static void main(String[] args) {
//Lock演示可重入锁
Lock lock = new ReentrantLock();
//创建线程
new Thread(() -> {
try {
//上锁
lock.lock();
System.out.println(Thread.currentThread().getName() + " 外层");
try {
//上锁
lock.lock();
System.out.println(Thread.currentThread().getName() + " 内层");
} finally {
//释放锁
lock.unlock();
}
} finally {
//释放锁
lock.unlock();
}
}, "t1").start();
//创建新线程
new Thread(() -> {
lock.lock();
System.out.println("aaaa");
lock.unlock();
}, "aa").start();
}
}
synchronized的可重入锁实现
package com.atguigu.self;
public class Test {
public static void main(String[] args) {
// synchronized
Object o = new Object();
new Thread(()->{
synchronized(o) {
System.out.println(Thread.currentThread().getName()+" 外层");
synchronized (o) {
System.out.println(Thread.currentThread().getName()+" 中层");
synchronized (o) {
System.out.println(Thread.currentThread().getName()+" 内层");
}
}
}
},"t1").start();
}
}
7.死锁
下面是一个死锁案例:
package com.atguigu.sync;
import java.util.concurrent.TimeUnit;
public class DeadLock {
//创建两个对象
static Object a = new Object();
static Object b = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (a) {
System.out.println(Thread.currentThread().getName()+" 持有锁a,试图获取锁b");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b) {
System.out.println(Thread.currentThread().getName()+" 获取锁b");
}
}
},"A").start();
new Thread(()->{
synchronized (b) {
System.out.println(Thread.currentThread().getName()+" 持有锁b,试图获取锁a");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a) {
System.out.println(Thread.currentThread().getName()+" 获取锁a");
}
}
},"B").start();
}
}
注意:程序运行但又没反应不一定是死锁,我们可以验证一个程序是否是死锁。
验证上方代码是否为死锁:使用jps -l和jstack命令
第一步:使程序处于运行中
第二步:按图示输入命令
之后程序会显示:
由此可知,上述为死锁。
8.Callable接口---没明白(使用了FutureTask)Callable有返回值,有异常。Runnable无返回值,无异常。
9.JUC的辅助类CountDownLatch---减少计数
需求:6个同学陆续离开教室之后班长锁门。
代码如下:下面是不使用CountDownLatch
package com.atguigu.self;
import java.util.concurrent.ExecutionException;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//6个同学陆续离开教室之后
for (int i = 1; i <=6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" 号同学离开了教室");
},String.valueOf(i)).start();
}
System.out.println(Thread.currentThread().getName()+" 班长锁门走人了");
}
}
1 号同学离开了教室
4 号同学离开了教室
main 班长锁门走人了
3 号同学离开了教室
2 号同学离开了教室
6 号同学离开了教室
5 号同学离开了教室Process finished with exit code 0
问题:出现了提前锁门
解决方案:
代码如下:
package com.atguigu.juc;
import java.util.concurrent.CountDownLatch;
//演示 CountDownLatch
public class CountDownLatchDemo {
//6个同学陆续离开教室之后,班长锁门
public static void main(String[] args) throws InterruptedException {
//创建CountDownLatch对象,设置初始值
CountDownLatch countDownLatch = new CountDownLatch(6);
//6个同学陆续离开教室之后
for (int i = 1; i <=6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" 号同学离开了教室");
//计数 -1
countDownLatch.countDown();
},String.valueOf(i)).start();
}
//等待
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+" 班长锁门走人了");
}
}
CycleBarrier---循环栅栏
需求:集齐七颗龙珠召唤神龙
package com.atguigu.juc;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
//集齐7颗龙珠就可以召唤神龙
public class CyclicBarrierDemo {
//创建固定值
private static final int NUMBER = 7;
public static void main(String[] args) {
//创建CyclicBarrier
CyclicBarrier cyclicBarrier =
new CyclicBarrier(NUMBER,()->{
System.out.println("*****集齐7颗龙珠就可以召唤神龙");
});
//集齐七颗龙珠过程
for (int i = 1; i <=7; i++) {
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" 星龙被收集到了");
//等待
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
Semaphore---信号灯
需求:6辆汽车,停3个车位
package com.atguigu.juc;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
//6辆汽车,停3个车位
public class SemaphoreDemo {
public static void main(String[] args) {
//创建Semaphore,设置许可数量
Semaphore semaphore = new Semaphore(3);
//模拟6辆汽车
for (int i = 1; i <=6; i++) {
new Thread(()->{
try {
//抢占
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" 抢到了车位");
//设置随机停车时间
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName()+" ------离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
10.读写锁---对读写锁存在疑惑
表锁为修改时整个表都锁柱,行锁为修改时那行数据被锁住。行锁会发生死锁。
读锁和写锁都会发生死锁。
package com.atguigu.readwrite;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
//资源类
class MyCache {
//创建map集合
private volatile Map map = new HashMap<>();
//创建读写锁对象
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
//放数据
public void put(String key,Object value) {
//添加写锁
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" 正在写操作"+key);
//暂停一会
TimeUnit.MICROSECONDS.sleep(300);
//放数据
map.put(key,value);
System.out.println(Thread.currentThread().getName()+" 写完了"+key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放写锁
rwLock.writeLock().unlock();
}
}
//取数据
public Object get(String key) {
//添加读锁
rwLock.readLock().lock();
Object result = null;
try {
System.out.println(Thread.currentThread().getName()+" 正在读取操作"+key);
//暂停一会
TimeUnit.MICROSECONDS.sleep(300);
result = map.get(key);
System.out.println(Thread.currentThread().getName()+" 取完了"+key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放读锁
rwLock.readLock().unlock();
}
return result;
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) throws InterruptedException {
MyCache myCache = new MyCache();
//创建线程放数据
for (int i = 1; i <=5; i++) {
final int num = i;
new Thread(()->{
myCache.put(num+"",num+"");
},String.valueOf(i)).start();
}
TimeUnit.MICROSECONDS.sleep(300);
//创建线程取数据
for (int i = 1; i <=5; i++) {
final int num = i;
new Thread(()->{
myCache.get(num+"");
},String.valueOf(i)).start();
}
}
}
11.读写锁的降级
写锁降级为读锁(读锁是不能升为写锁的),使写的同时可以读
package com.atguigu.readwrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
//演示读写锁降级
public class Demo1 {
public static void main(String[] args) {
//可重入读写锁对象
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();//读锁
ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();//写锁
//锁降级
//1 获取写锁
writeLock.lock();
System.out.println("atguigu");
//2 获取读锁
readLock.lock();
System.out.println("---read");
//3 释放写锁
writeLock.unlock();
//4 释放读锁
readLock.unlock();
}
}
12.阻塞队列---BlockingQueue
ArrayBlockingQueue:由数组结构组成的有界阻塞队列
linkedBlockingQueue:由链表结构组成的有界阻塞队列
DelayQueue:使用优先级队列实现的延迟无界阻塞队列
PriorityBlockingQueue:支持优先级排序的队列
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
linkedTransferQueue:由链表组成的无界阻塞队列
linkedBlockingQueue:由链表组成的双向阻塞队列
核心方法演示:
add():添加一个元素到队列中 返回boolean值 超过容量返回异常
element():返回队列中第一个元素
remove():删除第一个添加的元素 返回boolean值 超过容量返回异常
offer():添加一个元素到队列中 返回boolean值 超过容量返回false
poll():取出第一个添加的元素
put():添加一个元素到队列中 超过容量会阻塞
take():取出第一个添加的元素 超过容量会阻塞
offer("w",3L, TimeUnit.SECONDS):添加并且如果阻塞设置一定时间后关闭操作
//创建阻塞队列
BlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
//第一组
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
System.out.println(blockingQueue.element());
12.ThreadPool线程池
package com.atguigu.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//演示线程池三种常用分类
public class ThreadPoolDemo1 {
public static void main(String[] args) {
//一池五线程
ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //5个窗口
//一池一线程
ExecutorService threadPool2 = Executors.newSingleThreadExecutor(); //一个窗口
//一池可扩容线程
ExecutorService threadPool3 = Executors.newCachedThreadPool();
//10个顾客请求
try {
for (int i = 1; i <=10; i++) {
//执行
threadPool3.execute(()->{
System.out.println(Thread.currentThread().getName()+" 办理业务");
});
}
}catch (Exception e) {
e.printStackTrace();
}finally {
//关闭
threadPool3.shutdown();
}
}
}
代码中三种方式我们都不使用,一般使用自定义线程池方法
13.自定义线程池的方法
package com.atguigu.pool;
import java.util.concurrent.*;
//自定义线程池创建
public class ThreadPoolDemo2 {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
2L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
//10个顾客请求
try {
for (int i = 1; i <=10; i++) {
//执行
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" 办理业务");
});
}
}catch (Exception e) {
e.printStackTrace();
}finally {
//关闭
threadPool.shutdown();
}
}
}
14.分支合并框架
拆分再合并解决问题
package com.atguigu.forkjoin; import java.util.concurrent.*; class MyTask extends RecursiveTask15.异步回调{ //拆分差值不能超过10,计算10以内运算 private static final Integer VALUE = 10; private int begin ;//拆分开始值 private int end;//拆分结束值 private int result ; //返回结果 //创建有参数构造 public MyTask(int begin,int end) { this.begin = begin; this.end = end; } //拆分和合并过程 @Override protected Integer compute() { //判断相加两个数值是否大于10 if((end-begin)<=VALUE) { //相加操作 for (int i = begin; i <=end; i++) { result = result+i; } } else {//进一步拆分 //获取中间值 int middle = (begin+end)/2; //拆分左边 MyTask task01 = new MyTask(begin,middle); //拆分右边 MyTask task02 = new MyTask(middle+1,end); //调用方法拆分 task01.fork(); task02.fork(); //合并结果 result = task01.join()+task02.join(); } return result; } } public class ForkJoinDemo { public static void main(String[] args) throws ExecutionException, InterruptedException { //创建MyTask对象 MyTask myTask = new MyTask(0,100); //创建分支合并池对象 ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask forkJoinTask = forkJoinPool.submit(myTask); //获取最终合并之后结果 Integer result = forkJoinTask.get(); System.out.println(result); //关闭池对象 forkJoinPool.shutdown(); } }
package com.atguigu.completable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
//异步调用和同步调用
public class CompletableFutureDemo {
public static void main(String[] args) throws Exception {
//同步调用 无返回值
CompletableFuture completableFuture1 = CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName()+" : CompletableFuture1");
});
completableFuture1.get();
//mq消息队列
//异步调用 有返回值
CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+" : CompletableFuture2");
//模拟异常
int i = 10/0;
return 1024;
});
completableFuture2.whenComplete((t,u)->{
System.out.println("------t="+t); // 方法返回值
System.out.println("------u="+u); // 异常
}).get();
}
}



