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四个线程 虚假唤醒

JUC_多线程

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版的生产者和消费者问题
JUC_java_02
通过Lock找到Condition

JUC_java_03

代码实现:

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 精准通知和唤醒线程

JUC_多线程_04

代码测试:

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基本操作
JUC_i++_05

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

JUC_代码测试_06
1.可以有返回值

2.可以抛出异常

3.方法不同,run()/call()

代码测试
JUC_java_07

JUC_java_08

JUC_重入锁_09

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.结果可能需要等待,会阻塞