Lock锁
传统Synchronized
package com.cedric.JUC.demo;
public class SaleTicketDemo01 {
public static void main(String[] args) {
// 并发:多线程操作同一个资源类
Ticket ticket = new Ticket();
// 函数式接口,lambda表达式 (参数) -> {代码}
new Thread(()->{
for (int i = 1; i < 50; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 1; i < 50; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 1; i < 50; i++) {
ticket.sale();
}
},"C").start();
}
}
// 资源类 OOP
class Ticket{
// 属性、方法
private int number = 50;
// 卖票的方式
// synchronized 本质:队列,锁
public synchronized void sale(){
if(number > 0){
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "张票,还剩:" + number + "张" );
}
}
}
Lock锁
package com.cedric.JUC.demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest {
public static void main(String[] args) {
// 并发:多线程操作同一个资源类
Ticket2 ticket = new Ticket2();
new Thread(()->{ for(int i=1;i<50;i++) ticket.sale(); },"A").start();
new Thread(()->{ for(int i=1;i<50;i++) ticket.sale(); },"B").start();
new Thread(()->{ for(int i=1;i<50;i++) ticket.sale(); },"C").start();
}
}
// Lock三部曲
// 1.new ReentrantLock();
// 2.Lock.lock
// 3.finally => lock.unlock(); // 解锁
class Ticket2{
// 属性、方法
private int number = 50;
Lock lock = new ReentrantLock();
public void sale(){
lock.lock();//加锁
try{
// 业务代码
if(number > 0){
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "张票,还剩:" + number + "张" );
}
} catch (Exception e){
} finally{
lock.unlock(); //解锁
}
}
}
Synchronized 和 Lock区别
1.Synchronized 内置的java关键字,Lock是一个java类
2.Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
3.Synchronized 会自动释放锁,lock必须手动释放锁,如果不释放锁,死锁
4.Synchronized 线程1(获得锁、阻塞) 线程2(等待、傻傻的等);Lock锁就不一定会等待下去
5.Synchronized 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,默认非公平(可以自己设置)
6.Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的代码同步问题
锁是什么,如何判断锁的是谁?
生产者和消费者问题
生产者和消费者模式 Synchronized版
package com.cedric.JUC.pc;
public class Test {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for(int i=0;i<10;i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() -> {
for(int i=0;i<10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class Data{
private int num = 0;
// +1
public synchronized void increment() throws InterruptedException {
if(num != 0){
// 等待
this.wait();
}
System.out.println(Thread.currentThread().getName() + "-->" + ++num);
// 唤醒其他所有线程
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
if(num == 0){
// 等待
this.wait();
}
System.out.println(Thread.currentThread().getName() + "-->" + --num);
// 唤醒其他所有线程
this.notifyAll();
}
}
存在问题 A、B、C、D四个线程 虚假唤醒
if改为while判断
package com.cedric.JUC.pc;
public class Test {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for(int i=0;i<10;i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() -> {
for(int i=0;i<10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() -> {
for(int i=0;i<10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() -> {
for(int i=0;i<10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{
private int num = 0;
// +1
public synchronized void increment() throws InterruptedException {
while(num != 0){
// 等待
this.wait();
}
System.out.println(Thread.currentThread().getName() + "-->" + ++num);
// 唤醒其他所有线程
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
while(num == 0){
// 等待
this.wait();
}
System.out.println(Thread.currentThread().getName() + "-->" + --num);
// 唤醒其他所有线程
this.notifyAll();
}
}
JUC版的生产者和消费者问题
通过Lock找到Condition
代码实现:
package com.cedric.JUC.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test2 {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data2.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data2.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data2.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data2.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data2 {
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// condition.await(); // 等待
// condition.signalAll();// 唤醒全部
// +1
public void increment() throws InterruptedException {
try {
lock.lock();
// 业务代码
while (num != 0) {
// 等待
condition.await();
}
System.out.println(Thread.currentThread().getName() + "-->" + ++num);
// 唤醒其他所有线程
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// -1
public void decrement() throws InterruptedException {
try {
lock.lock();
while (num == 0) {
// 等待
condition.await();
}
System.out.println(Thread.currentThread().getName() + "-->" + --num);
// 唤醒其他所有线程
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition 精准通知和唤醒线程
代码测试:
package com.cedric.JUC.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test3 {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printC();
}
},"C").start();
}
}
class Data3{ // 资源类lock
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1; // 1A 2B 3C
// +1
public void printA() {
lock.lock();
try {
// 业务,判断->执行->通知
while(number != 1){
// 等待
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "-->AAAAAAAAAA");
//精准唤醒
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
// 业务,判断->执行->通知
while(number != 2){
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "-->BBBBBBBBB");
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
// 业务,判断->执行->通知
while(number != 3){
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "-->CCCCCCCCC");
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
八锁现象
new this 具体的一个手机
static Class唯一的一个模板
集合类不安全
List
package com.cedric.JUC.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
// java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
public static void main(String[] args) {
// 并发下 ArrayList不安全
/**
* 解决方案:
* 1.List<String> list = new Vector<>();
* 2. List<String> list = Collections.synchronizedList(new ArrayList<>());
* 3.List<String> list = new CopyOnWriteArrayList<>();
*/
// CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略
// 多个线程调用的时候,list,读取的时候,是固定的,写入(覆盖)
// 在写入的时候避免覆盖,造成数据问题
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
}).start();
}
}
}
Set不安全
package com.cedric.JUC.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* 同理可证:java.util.ConcurrentModificationException
* 1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
* 2.
*/
public class SetTest {
public static void main(String[] args) {
// Set<String> set = new HashSet<>();
// Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set =new CopyOnWriteArraySet<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
HashSet底层是什么?
public HashSet() {
map = new HashMap<>();
}
// add set本质就是 map key是无法重复的
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object(); // 不变的值
Map不安全
回顾Map基本操作
package com.cedric.JUC.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
// java.util.ConcurrentModificationException
public class MapTest {
public static void main(String[] args) {
// Map<String,String> map = new HashMap<>();
// Map<String,String> map = Collections.synchronizedMap(new HashMap<>());
Map<String,String> map = new ConcurrentHashMap<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
Callable
1.可以有返回值
2.可以抛出异常
3.方法不同,run()/call()
代码测试
package com.cedric.JUC.unsafe;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//new Thread(new Runnable()).start();
// new Thread(new FutureTask<V>()).start();
// new Thread(new FutureTask<V>( Callable )).start();
new Thread().start();
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread);
// 适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start(); // 结果会被缓存,效率高
Integer o = (Integer)futureTask.get(); //获取 Callable的返回结果 get方法可能会产生阻塞,把他放到最后
System.out.println(o);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
细节:
1.有缓存
2.结果可能需要等待,会阻塞