JAVA语言学习-Day10、11、12
1.什么是JUC
java.util工具包、包、分类
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
2.线程和进程
举例:开启一个Typora(进程),输入、自动保存(线程)
进程:一个程序
一个进程往往可以包含多个线程,至少包含一个
线程:写字、自动保存
Java默认有两个进程:main、GC
2.1 并发和并行
并发:多个线程同时操作同一个资源
- 
CPU一核 ,模拟出多线程(快速交替) 
并行:多个线程同时操作不同资源
- 
CPU多核 ,多个线程同时执行;线程池 
//CPU密集型,IO密集型
Runtime.getRuntime().availableProcessors();//获取CPU核数
并发编程的本质:充分利用CPU的资源
- 
线程的状态:New、Runable、Blocked、Waiting、Time_waiting、Terminated 
- 
wait/sleep区别 - 
wait-Object sleep-Thread //来自不同的类 
- 
wait会释放锁 sleep不会 
- 
wait必须在同步代码块 sleep都可以 
- 
wait不需要捕获异常 sleep必须捕获异常 
 
- 
3.Lock锁
- 
ReentrantLock 
- 
ReentrantReadWriteLock.ReadLock 
- 
ReentrantReadWriteLock.WriteLock 
FairSync公平锁:可以先来后到
NonfairSync非公平锁:可以插队(默认)
//Lock三步
//new ReentrantLock();
//lock.lock();//加锁
//lock.unlock();//解锁
3.1 Synchronize和Lock的区别
- 
Synchronize是Java内置的关键字;Lock是一个Java类 
- 
Synchronize无法判断获取锁的状态;Lock可以判断是否获取到了锁 
- 
Synchronize会自动释放锁;Lock必须要手动释放锁,若果不释放,会造成死锁 
- 
Synchronize线程线形执行;Lock锁不一定会持续等待 
- 
Synchronize可重入锁,不可中断的,非公平;Lock可重入锁,可以判断锁,可以设置是否公平 
- 
Synchronize适合锁少量的代码同步问题;Lock适合锁大量的同步代码 
4. 八锁现象
- 
new出来的锁的具体对象 
- 
static锁的class模板 
/**
 * 8锁问题
 * 1.单个类中存在两个同步锁方法,线程分别调用,谁先执行?
 * 2.类中存在某个方法延迟,谁先执行?
 * 3.增加了一个普通方法,谁先执行?
 * 4.两个对象,调用两个同步方法,谁先执行?
 * 5.单个类中存在两个静态的同步方法,线程分别调用,谁先执行?
 * 6.两个对象,调用静态的两个同步方法,谁先执行?
 * 7.单个类中,一个静态同步方法,一个普通同步方法,谁先执行?
 * 8.两个对象,一个执行静态同步方法,一个执行普通同步方法,谁先执行?
 */
public class Test01 {
    public static void main(String[] args) {
        Phone p = new Phone();
        Phone p2 = new Phone();
        new Thread(()->{p.sendMsg();},"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//        new Thread(()->{p.call();},"B").start(); 问题1、2
//        new Thread(()->{p.hello();},"B").start(); 问题3
//        new Thread(()->{p2.call();},"B").start(); 问题4
    }
}
//synchronized锁的是方法的调用者
//两个方法用的是同一个锁,谁先拿到谁先执行
//只有一个唯一的class对象,static静态方法,类一家在就有了。锁的是class魔板
class Phone{
    public synchronized void sendMsg(){
//  public static synchronized void sendMsg(){问题5
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发送信息");
    }
    public synchronized void call(){
//  public static synchronized void call(){问题5
        System.out.println("拨打电话");
    }
    public void hello(){
        System.out.println("普通方法hello");
    }
}
5. 集合类不安全
并发下ArrayList不安全
- 
java.util.ConcurrentModificationException 并发修改异常 
处理方法
- 
List<String> list = new Vector<>();//不推荐
- 
List<String> list = Collections.synchronizedList(new ArrayList<>());//父类同步锁处理
- 
List<String> list = new CopyOnWriteArrayList<>();
 //JUC中提供方法处理.写入时复制 COW 计算机程序设计领域的一种优化策略
 //多个线程调用的时候,list,读取的时候固定的,写入(覆盖)
 //在写入的时候避免覆盖,造成数据问题。
Set不安全
处理方法
- 
Set<String> list = Collections.synchronizedSet(new HashSet<>());
- 
Set<String> list = new CopyOnWriteArraySet<>();
Map不安全
处理方法
- 
Map<String,String> map = Collections.synchronizedMap(new HashMap<>());
- 
Map<String,String> map = new ConcurrentHashMap<>();
6.Callable
- 
可以由返回值 
- 
可以抛出异常 
- 
方法不同,call() 
public class TestCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread mt = new MyThread();
        //适配类
        FutureTask fuTask = new FutureTask(mt);
        new Thread(fuTask,"callable调用").start();
        new Thread(fuTask,"callable调用2").start();//结果会被缓存,效率高
        String ret = (String)fuTask.get();  //获取callable的返回结果,可能会产生阻塞,放到最后或者异步
        System.out.println("返回值==="+ret);
    }
}
class MyThread implements Callable<String>{
    @Override
    public String call() throws Exception {
        System.out.println("调用callable");
        return "返回测试";
    }
}
7.常用的辅助类
7.1、countDownLatch
允许一个线程或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助
//减法计数器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        //总数是6,必须执行任务的时候,再使用!
        CountDownLatch cdl = new CountDownLatch(6);
        for (int i=0;i<6;i++){
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" Go Out");
                cdl.countDown();
            },"线程"+i).start();
        }
        cdl.await();//等待计数器归零,然后再向下执行
        System.out.println("流程结束");
    }
}
7.2、CyclicBarrier
允许一组线程全部等待彼此达到共同屏障点的同步辅助
//加法计数器
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier clb = new CyclicBarrier(7,()->{
            System.out.println("====开始执行====");
        });
        for (int i=0;i<7;i++){
            final int temp = i;
            new Thread(()->{
                System.out.println("当前为线程:"+temp);
                try {
                    clb.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}
7.3、Semaphore
一个计数信号量。在概念上,信号量维持一组许可证。如果有必要,每个acquiere()都会阻塞,直到许可证可用,然后才能使用它。
public class SemaphoreDemo {
    public static void main(String[] args) {
        //线程数量,限流
        Semaphore sp = new Semaphore(3);
        for (int i=0;i<6;i++){
            new Thread(()->{
                try {
                    sp.acquire();//acquire() 得到
                    System.out.println("当前线程:"+ Thread.currentThread().getName());
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    sp.release();//release() 释放
                }
            },String.valueOf(i)).start();
        }
    }
}
8.读写锁
ReadWriteLock
一个ReadWriteLock维护一对关联的locks,一个用于只读操作,一个用于写入。read lock可以由多个阅读器线程同时进行,只要没有作者。write lock是独家的。
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        Mycache mycache = new Mycache();
        for (int i=1;i<=10;i++){
            final int temp = i;
            new Thread(()->{
                mycache.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }
        for (int i=1;i<=10;i++){
            final int temp = i;
            new Thread(()->{
                mycache.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}
/**
 * 自定义缓存
 */
class Mycache{
    private volatile Map<String, Object> map = new HashMap<>();
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    //存,写
    public void put(String key,Object value){
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"写入:"+key+"开始");
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+"写入:"+key+"完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    };
    //取,读
    public void get(String key){
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"读取:"+key+"开始");
            Object obj = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取:"+key+"完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    };
}
9.阻塞队列
阻塞队列:BlockingQueue
什么时候会用阻塞队列:多线程并发处理 线程池
- 
队列queue - 
Deque 双端队列 
- 
BlockingQueue 阻塞队列 - 
ArrayBlockingQueue 
- 
LinkedBlockingQUeue 
 
- 
- 
AbstractQueue 非阻塞队列 
 
- 
9.1四组API
| 方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞/等待 | 超时等待 | 
|---|---|---|---|---|
| 添加 | add | offer | put | offer("a", 2, TimeUnit.SECONDS) | 
| 移除 | remove | poll | take | poll(2, TimeUnit.SECONDS) | 
| 检测队首元素 | element | peek | - | - | 
9.2同步队列SynchronousQueue
没有容量,进去一个元素,必须等待取出来之后,才能往里面放一个元素
BlockingQueue<String> bq = new SynchronousQueue<>();//同步队列
new Thread(()->{
    try {
        System.out.println(Thread.currentThread().getName()+" put1 ");
        bq.put("1");
        System.out.println(Thread.currentThread().getName()+" put2 ");
        bq.put("2");
        System.out.println(Thread.currentThread().getName()+" put3 ");
        bq.put("3");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
},"线程1").start();
new Thread(()->{
    try {
        TimeUnit.SECONDS.sleep(1);
        System.out.println(Thread.currentThread().getName()+"=>"+bq.take());
        System.out.println(Thread.currentThread().getName()+"=>"+bq.take());
        System.out.println(Thread.currentThread().getName()+"=>"+bq.take());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
},"线程2").start();
10. 线程池
程序运行的本质:占用系统的资源。优化资源的使用=>池化技术
线程池、连接池、内存池、对象池。。。。
池化技术:事先准备好一些资源,使用时取出,使用后归还
10.1线程池的优点
- 
降低资源的消耗 
- 
提高响应的速度 
- 
方便管理 
线程复用,可以控制最大并发数,管理线程
10.2 三大方法、七大参数、四种拒绝策略
- 
三大方法 
//Executors 工具类,3大方法
public class ThreadPoolDemo {
    public static void main(String[] args) {
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定的线程池大小
        ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的
        try {
            for (int i=0;i<100;i++){//使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{System.out.println(Thread.currentThread().getName());});
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            threadPool.shutdown();//线程池用完,程序结束,关闭线程池
        }
    }
}
- 
七大参数及自定义线程池 
方法本质调用的ThreadPooExecutor()
int corePoolSize    //核心线程池大小
int maximumPoolSize //最大核心线程池大小
long keepAliveTime  //超时没有调用 释放
TimeUnit unit       //超时单位
BlockingQueue<Runnable> workQueue   //阻塞队列
ThreadFactory threadFactory         //线程工厂:创建线程的,一般不用动
RejectedExecutionHandler handler    //拒绝策略
ExecutorService threadPool = new ThreadPoolExecutor(2,5,3,
        TimeUnit.SECONDS,new LinkedBlockingDeque<>(),Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());
- 
四种拒绝策略 - 
new ThreadPoolExecutor.AbortPolicy()//拒绝策略,超出异常 java.util.concurrent.RejectedExecutionException
- 
new ThreadPoolExecutor.CallerRunsPolicy()//哪来的去哪里
- 
new ThreadPoolExecutor.DiscardPolicy()//队列满了,丢掉任务,不会抛出异常
- 
new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试去和最早的竞争,竞争失败抛掉,不会抛出异常
 
- 
10.3 池的大小如何设置
- 
CPU密集型 - 
Runtime.getRuntime().availableProcessors();//获取CPU的核数
 
- 
- 
IO密集型 - 
判断程序中十分耗IO的线程,一般设置为两倍 
 
- 
11.四大函数式接口
Consumer、Function、Predicate、Supplier //四大函数式接口
新时代的程序员:lambda表达式、链式编程、函数式接口、Sream流式计算
- 
函数式接口:只有一个方法的接口 
- 
Function:函数型接口,有一个输入参数,一个输出 
Function<String,String> f = (str)->{return str;};
f.apply("");
- 
Predicate:断定型接口,有一个输入参数,返回值只能是boolean 
Predicate<String> p = (str)->{return str.isEmpty();};
p.test("null");
- 
Consumer:消费型接口,只有输入,没有返回值 
Consumer<String> c = (str)->{};
c.accept("");
- 
Supplier:供给型接口,没有参数,只有返回值 
Supplier<String> s = ()->{return "";};
String str = s.get();
12.流式计算
集合、MySQL的本质就是存储,计算都应该交给流来操作
public class StreamTest {
    public static void main(String[] args) {
        User u1 = new User(1,"a",21);
        User u2 = new User(2,"b",22);
        User u3 = new User(3,"c",23);
        User u4 = new User(4,"d",24);
        User u5 = new User(5,"e",25);
        User u6 = new User(6,"f",26);
        List<User> list = Arrays.asList(u1,u2,u3,u4,u5,u6);
        list.stream()
                .filter(u->{return u.getId()%2==0;})
                .filter(u->{return u.getAge()>23;})
                .map((u)->{return u.getName().toUpperCase();})
                .sorted((uu1,uu2)->{return uu1.compareTo(uu2);})
                .limit(1)
                .forEach(System.out::println);
    }
}
13. ForkJoin
分支合并
ForkJoin在JDK1.7,并行执行任务!提高效率,大数据量。
特点:工作窃取
维护的都是双端队列
public static void forkJoinTask() throws ExecutionException, InterruptedException {
    long start = System.currentTimeMillis();
    ForkJoinPool fjp = new ForkJoinPool();
    ForkJoinTask<Long> task = new ForkJoinDemo(0L,1_000_000_000L);
    ForkJoinTask<Long> submit = fjp.submit(task);
    Long ret = submit.get();
    long end = System.currentTimeMillis();
    System.out.println("计算耗费时间:"+(end-start));
    //计算耗费时间:5143
}
public class ForkJoinDemo extends RecursiveTask<Long> {
    private Long start;
    private Long end;
    private Long temp = 10_000L;
    public ForkJoinDemo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }
    @Override
    protected Long compute() {
        if ((end - start)<temp) {
            Long sum = 0L;
            for(Long i=start;i<end;i++){
                sum += i;
            }
            return sum;
        } else {
            long mid = (start+end)/2;//中间值
            ForkJoinDemo fjd1 = new ForkJoinDemo(start,mid);
            fjd1.fork();//拆分任务,把任务压入线程队列
            ForkJoinDemo fjd2 = new ForkJoinDemo(mid+1,end);
            fjd2.fork();//拆分任务,把任务压入线程队列
            return fjd1.join() + fjd2.join();
        }
    }
}
14. 异步回调
Future设计的初衷:对将来的某个事件的结果进行建模
/**
 * 异步调用:CompletableFuture
 * 1.异步执行
 * 成功回调
 * 失败回调
 */
public class FutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //没有返回值的 runAsync 异步回调
//        CompletableFuture<Void> cf = CompletableFuture.runAsync(()->{
//            try {
//                TimeUnit.SECONDS.sleep(2);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            } finally {
//                System.out.println(Thread.currentThread().getName()+"runAsync=>void");
//            }
//        });
//        System.out.println(Thread.currentThread().getName()+"执行");
//        cf.get();//获取阻塞执行结果
        //有返回值的异步回调
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            return 1119;
        });
        cf1.whenComplete((t, u) -> {
            System.out.println("t=>" + t);//正常的返回结果
            System.out.println("u=>" + u);//errInfo
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 404;
        }).get();
    }
}
15.JMM
Volatile是Java虚拟机提供的轻量级的同步机制
- 
保证可见性 
- 
不保证原子性 
- 
禁止指令重排 
JMM:java内存模型,是一种概念、约定,不是实际存在的东西
- 
关于JMM的一些同步的约定 - 
线程解锁前:必须把共享变量立刻刷回主存 
- 
线程加锁前:必须读取主存中的最新值到工作内存中 
- 
加锁和解锁是同一把锁 
 
- 
线程:工作内存、主内存
八个操作:
主存->read->load->工作内存 <-assign-use>执行引擎
工作内存->write->store->主存
lock、unlock

存在问题,线程B修改了内存值,但线程A无法及时发现
16. Volatile
- 
保证可见性 
public class VolatileDemo {
    //不加volatile 程序就回死循环
    private volatile static int num = 0;
    public static void main(String[] args) {
        new Thread(()->{//线程对主内存的变化不可知
            while (num == 0){}
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
    }
}
- 
不保证原子性 
原子性:不可分割
线程A再执行任务时,不可被打扰,也不能被分割。要么同时成功,要么同时失败。
private volatile static int num = 0;
public static void add(){
    num++;
}
public static void main(String[] args) {
    for (int i = 0; i < 20; i++) {
        new Thread(()->{
            for (int j = 0; j < 1000; j++) {
                add();
            }
        }).start();
    }
    while (Thread.activeCount()>2){
        Thread.yield();
    }
    System.out.println(Thread.currentThread().getName()+" "+num);
}
如果不加lock和synchronize,怎样保证原子性
使用atomic原子类,解决原子性问题
- 
禁止指令重排 
指令重排:写的程序,计算机并不是按照写的那样去执行的。
源代码--->编译器优化重排--->指令并行也可能会重排--->内存系统也会重排--->执行
处理器在执行指令重排的时候,考虑:数据间的依赖性!
volatile可以避免指令重排:
内存屏障。CPU指令,作用
- 
保证特定操作的执行顺序 
- 
可以保证某些变量的内存可见性(利用这些特性,vlatile实现了可见性) 
17.单例模式
饿汉式、DCL懒汉式
//懒汉式单例
public class LazyDemo {
    private static boolean flag = false;
    private LazyDemo() {
        System.out.println(Thread.currentThread().getName() + "  ");
        synchronized (LazyDemo.class){
            if (flag == false){
                flag = true;
            }else{
                throw new RuntimeException("反射破坏异常");
            }
            if (ld != null) {
                throw new RuntimeException("反射破坏异常");
            }
        }
    }
    private volatile static LazyDemo ld;
    //双重检测锁模式的 懒汉式单例 DCL懒汉式
    public static LazyDemo getInstance(){
        if (ld == null) {//多线程并发存在问题
            synchronized (LazyDemo.class) {
                if (ld == null) {
                    /**
                     * 1.分配内存空间
                     * 2.执行构造方法,初始化对象
                     * 3.把这个对象指向对象空间
                     */
                    ld = new LazyDemo();//不是原子性操作
                }
            }
        }
        return ld;
    }
}
//enum本身也是一个class类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance() {
        return INSTANCE;
    }
}
class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle es1 = EnumSingle.INSTANCE;
        EnumSingle es2 = EnumSingle.INSTANCE;
        //Cannot reflectively create enum不能使用反射破坏枚举
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle es3 = declaredConstructor.newInstance();
        System.out.println(es1);
        System.out.println(es2);
        System.out.println(es3);
    }
}
18.CAS
cas:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作,如果不是就一直循环。
缺点:
- 
循环会耗时 
- 
一次性只能保证一个共享变量的原子性 
- 
ABA问题 
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        //期望、更新
        //如果期望的值匹配,那么就更新,否则不更新
        //CAS是CPU的并发原语
        atomicInteger.compareAndSet(2020,2024);
        System.out.println(atomicInteger.get());
    }
}
CAS:ABA问题(狸猫换太子)
//ABA问题
atomicInteger.compareAndSet(2020,2024);
System.out.println(atomicInteger.get());
atomicInteger.compareAndSet(2024,2020);
System.out.println(atomicInteger.get());
//期望的线程
atomicInteger.compareAndSet(2020,2024);
System.out.println(atomicInteger.get());
19. 原子引用解决ABA问题
//AtomicStampedReference 注意:如果泛型是一个包装类,注意对象引用问题
    //正常在业务操作,比较的都是一个个对象
    AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
    new Thread(()->{
        int stamp = atomicStampedReference.getStamp();
        System.out.println(Thread.currentThread().getName()+" 1: "+stamp);
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(atomicStampedReference.compareAndSet(1, 2,
                atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
        System.out.println(Thread.currentThread().getName()+" 2: "+atomicStampedReference.getStamp());
        System.out.println(atomicStampedReference.compareAndSet(2, 1,
                atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
        System.out.println(Thread.currentThread().getName()+" 3: "+atomicStampedReference.getStamp());
    },"a").start();
    new Thread(()->{
        int stamp = atomicStampedReference.getStamp();
        System.out.println(Thread.currentThread().getName()+" 1: "+stamp);
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(atomicStampedReference.compareAndSet(1, 4, stamp, stamp + 1));
        System.out.println(Thread.currentThread().getName()+" 2: "+atomicStampedReference.getStamp());
    },"b").start();
}
20.各种锁
- 
公平锁、非公平锁 
公平锁:不能插队
非公平锁:可以插队
- 
可重入锁(递归锁) 
- 
自旋锁 

- 
死锁排查 
- 
使用jps定位进程号:jps -l 
- 
使用jstack进程号找到死锁问题 jstack 进程号 
案例
单例模式、排序算法、生产者消费者问题、死锁
生产者消费者问题
Synchronice锁实现
/**
 * 线程间的通信问题:生产者消费者问题! 等待唤醒 通知唤醒
 * 线程交替执行 A B操作同一个变量 num=0
 * A num+1
 * B num-1
 */
public class ProducerConsumer {
    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 number = 0;
    //只要是并发编程,一定要有锁
    //if判断一次,存在虚假唤醒问题。等待一般出现在while循环中
    public synchronized void increment() throws InterruptedException {
        while(number!=0){
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        while(number==0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }
}
lock锁实现及优化
/**
 * Lock锁实现生产者消费者问题
 * 精准的通知和唤醒线程
 */
public class ProducerConsumer01 {
    public static void main(String[] args) {
//        Data01 data = new Data01();
//        //Lock锁实现生产者消费者问题
//        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();
        Data02 data = new Data02();
        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 Data01{ //数字、资源类
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
//    condition.await();等待
//    condition.signalAll();唤醒全部
    public synchronized void increment() throws InterruptedException {
        lock.lock();
        try {
            while(number!=0){
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public synchronized void decrement() throws InterruptedException {
        lock.lock();
        try {
            while(number==0){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
class Data02{
    private Lock lock = new ReentrantLock();
    private Condition condition01 = lock.newCondition();
    private Condition condition02 = lock.newCondition();
    private Condition condition03 = lock.newCondition();
    private int number = 1;
    public void printA(){
        lock.lock();
        try {
            while (number!=1){
                condition01.await();
            }
            System.out.println("当前执行的线程:"+Thread.currentThread().getName()+",number:"+number);
            number = 2;
            condition02.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {
            while (number!=2){
                condition02.await();
            }
            System.out.println("当前执行的线程:"+Thread.currentThread().getName()+",number:"+number);
            number=3;
            condition03.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {
            while (number!=3){
                condition03.await();
            }
            System.out.println("当前执行的线程:"+Thread.currentThread().getName()+",number:"+number);
            number=1;
            condition01.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号