zoukankan      html  css  js  c++  java
  • JUC01

    JUC01

    java 线程的高级篇

    1.什么是JUC?

    java.util工具包,包,分类

    2.线程与进程

    进程:一个程序,QQ.exe,Music.exe程序的集合

    一个进程往往可以包含多个线程,至少包含一个!

    Java默认有几个线程?2个,main GC

    线程:开了一个进程Typora, 里面有写字,自动保存等是线程负责的

    Java真的可以开线程吗?不能,开不了

    public class Test1 {
        public static void main(String[] args) {
            new Thread().start();
        }
    }
    

    看源码!只能通过本地方法去调

    public synchronized void start() {
    
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
    
        group.add(this);
    
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
              
            }
        }
    }
    
    private native void start0();//只能通过本地方法去调
    
    并行   并发
    

    并发(多线程同时操作同一个资源)

    • CPU一核,模拟出来多条线程,唯快不破,快速交替

    并行(多个人一起行走)

    • CPU多核,多个线程可以同时执行 线程池
    package com.mjh;
    
    public class Test1 {
        public static void main(String[] args) {
            //获取CPU的核数
            //cpu密集型  IO密集型
            System.out.println(Runtime.getRuntime().availableProcessors());
        }
    }
    

    并发的本质:充分利用CPU资源

    线程有几个状态
    
    public enum State {
       //新生
        NEW,
    
      //运行
        RUNNABLE,
        
      //阻塞
        BLOCKED,
    
       //等待   死死的等
        WAITING,
    
      //超时等待
        TIMED_WAITING,
    
       //终止
        TERMINATED;
    }
    

    3.Lock锁(重要)

    为了解决多线程的并发问题

    传统的 synchronized 本质就是队列 锁

    package com.mjh;
    
    public class Test1 {
        public static void main(String[] args) {
         //并发  多线程操作同一个资源类,把资源类丢入线程
            Ticket ticket = new Ticket();
    
            //@FunctionalInterface  函数式接口, JDK1.8  lambda表达式(参数)——>{代码}
           /* new Thread(new Runnable() {
                @Override
                public void run() {
    
                }
            }).start();*/
    
            new Thread(()->{
                for (int i = 0; i <40 ; i++) {
                    ticket.sale();
                }
                },"A").start();
            new Thread(()->{
                for (int i = 0; i <40 ; i++) {
                    ticket.sale();
                }
            },"B").start();
            new Thread(()->{for (int i = 0; i <40 ; i++) {
                ticket.sale();
            }
            },"C").start();
    
    
        }
    }
    class Ticket{
        //属性,方法
        private int number = 30;
    
        //卖票的方式
        // synchronized  同步  解决多个人同时抢而造成票数混杂问题  等一个线程执行完了再执行下一个线程
        public synchronized void  sale(){
            if(number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
            }
        }
    }
    

    lock 接口

    公平锁:十分公平 先来后到

    非公平锁:十分不公平 可以插队

    package com.mjh;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Test2 {
    
        public static void main(String[] args) {
            //并发  多线程操作同一个资源类,把资源类丢入线程
          Ticket2 ticket = new Ticket2();
    
            //@FunctionalInterface  函数式接口, JDK1.8  lambda表达式(参数)——>{代码}
            new Thread(()->{ for (int i = 0; i <40 ; i++) ticket.sale(); },"A").start();
            new Thread(()->{ for (int i = 0; i <40 ; i++) ticket.sale(); },"B").start();
            new Thread(()->{ for (int i = 0; i <40 ; i++) ticket.sale(); },"C").start();
        }
        }
    
        //1.new ReentrantLock();
        //2. lock.lock();//加锁
        // finally   ---->   lock.unlock();//解锁
    
        class Ticket2 {
            //属性,方法
            private int number = 30;
            Lock lock = new ReentrantLock();
    
            //卖票的方式
            public void sale() {
    
                try {
                    lock.lock();//加锁
                    if (number > 0) {
                        System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
                    }
                } finally {
                    lock.unlock();//解锁
    
                }
            }
    }
    

    synchronized 和lock的区别

    1.synchronized 是内置的java关键字,lock是一个类

    2.synchronize无法判断获取锁的状态,lock 可以判断是否获取到了锁

    3.synchronize会自动释放锁,lock必须要手动释放锁!如果不释放锁,就变成了死锁

    4.synchronize 线程1(获得锁,阻塞)、线程2(等待,傻傻的等);lock 就不一定会等待下去,它可以等不了就下了

    5.synchronize 可重入锁,不可以中断,非公平 ;lock 可重入锁,可以判断锁,非公平(可以自设置);

    6.synchronize 适合少量的代码同步问题,lock 适合大量的同步代码

    锁是什么,如何判断锁的是谁

    4.生产者和消费者

    传统的synchronized
    package com.mjh;
    
    public class pc {
        public static void main(String[] args)  {
            Date date = new Date();
            new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    try {
                        date.increment();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"A").start();
    
            new Thread(()->{
                for (int i = 0; i <10 ; i++) {
                    try {
                        date.decrement();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"B").start();
        }
    }
    
    class  Date{
        private int number= 0;
        public  synchronized void increment() throws InterruptedException {
            if(number!=0){
                //等待其他线程使用为0为止
                this.wait();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程,我+1结束了
            this.notifyAll();
        }
        public synchronized void decrement() throws InterruptedException {
            if(number==0){
                //等待其他线程生产在使用
                this.wait();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程,我-1结束了
            this.notifyAll();
        }
    }
    

    如果有多个线程呢?如果用以上代码,运行出来就会出现如下情况,if就判断了一次,这样多个线程同时去访问同一个资源他也不知道,导致同时都加了1,导致了虚假唤醒

    按照官网所说如下图。使用while循环会让一个线程进去,而其他线程等待,因此稍改变代码如下

    JUC版的生产者和消费者

    任何一个新的技术,绝对不是仅仅只是覆盖了原来的技术,一定会有优势和补充

    Condition实现精准通知和唤醒线程

    使用多个监视器就可以实现顺序执行线程

    package com.mjh.pc;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class PC3 {
        public static void main(String[] args) {
            Date3 date=new Date3();
            new Thread(()->{
                for (int i = 0; i <10; i++) {
                    date.printA();
                }
            },"A").start();
            new Thread(()->{
                for (int i = 0; i <10; i++) {
                    date.printB();
                }
            },"B").start();
            new Thread(()->{
                for (int i = 0; i <10; i++) {
                    date.printC();
                }
            },"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 number=1;//1A  2B 3C
    
        public void printA(){
            lock.lock();
            try {
                //业务代码
                while(number!=1){
                    //等待
                    condition.await();
                }
                number=2;
                System.out.println(Thread.currentThread().getName()+"=>AAAAAAAAA");
                condition2.signal();//唤醒当前线程
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void  printB(){
            lock.lock();
            try {
                while(number!=2){
                    condition2.await();
                }
                number=3;
                System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBBBB");
                condition3.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void printC(){
            lock.lock();
            try {
                //业务代码
                while(number!=3){
                    //等待
                    condition3.await();
                }
                number=1;
                System.out.println(Thread.currentThread().getName()+"=>CCCCCCCCCCC");
                condition.signal();//唤醒当前线程
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    

    5.8锁现象彻底理解锁

    我们回到“锁是什么,如何判断锁是谁”-------->永远知道什么是锁,锁谁

    对象,class

    package com.mjh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     *8锁,就是关于锁的8个问题
     * 1.标准情况下,两个线程先打印发短信还是打电话? 1/发短信  2/打电话
     * 2.sendSms延迟4秒,两个线程先打印发短信还是打电话? 1/发短信  2/打电话
     */
    public class Test1 {
        public static void main(String[] args) {
            Phone phone = new Phone();
    
            // phone.sendSms();这不是先调用的问题,是锁的存在
            new Thread(()->{
                phone.sendSms();
            },"A").start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()->{
                phone.call();
            },"B").start();
        }
    }
    
    class Phone{
        //synchronized 锁的对象是方法的调用者
        //两个方法用的是同一个锁,谁先拿到谁执行
    
        public synchronized void sendSms(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
    
        }
        public synchronized void call(){
            System.out.println("打电话");
    
        }
    }
    
    package com.mjh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 3.增加了一个普通方法下,两个线程先打印你好还是发短信? 1/你好  2/发短信
     * 4.两个线程两个对象,一个线程调用一个对象先打印打电话还是发短信? 1/打电话 2/发短信
     */
    public class Test2 {
        public static void main(String[] args) {
            Phone2 phone = new Phone2();
            Phone2 phone2 = new Phone2();
    
    
            // phone.sendSms();这不是先调用的问题,是锁的存在
            new Thread(()->{
                phone.sendSms();
            },"A").start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()->{
                phone2.call();
            },"B").start();
        }
    }
    
    class Phone2{
        //synchronized 锁的对象是方法的调用者
        //两个方法用的是同一个锁,谁先拿到谁执行
    
        public synchronized void sendSms(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
    
        }
        public synchronized void call(){
            System.out.println("打电话");
        }
    
        //这里没有锁
        public void hello(){
            System.out.println("你好");
        }
    }
    
    package com.mjh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     *  5.两个静态方法,一个对象,先打印打电话还是发短信? 1/打电话 2/发短信
     * 6.两个静态方法,两个线程两个对象,一个线程调用一个对象先打印打电话还是发短信? 1/打电话 2/发短信
     *
     */
    public class Test3 {
        public static void main(String[] args) {
            Phone3 phone = new Phone3();
            Phone3 phone3 = new Phone3();
    
    
            // phone.sendSms();这不是先调用的问题,是锁的存在
            new Thread(()->{
                phone.sendSms();
            },"A").start();
    
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()->{
                phone3.call();
            },"B").start();
        }
    }
    //Phone3唯一的一个Class对象
    class Phone3{
        //synchronized 锁的对象是方法的调用者
        //static 静态方法
        //类一加载就有了,锁的是class
        public static synchronized void sendSms(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
    
        }
        public static synchronized void call(){
            System.out.println("打电话");
        }
    
    }
    
    package com.mjh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 7.1个静态同步方法,1个普通同步方法,一个对象,先打印发短信?还是打电话 1/打电话 2/发短信
     * 8.1个静态同步方法,1个普通同步方法,两个对象,先打印发短信?还是打电话 1/打电话 2/发短信
     */
    public class Test4 {
        public static void main(String[] args) {
            Phone4 phone = new Phone4();
            Phone4 phone2 = new Phone4();
    
    
            // phone.sendSms();这不是先调用的问题,是锁的存在
            new Thread(()->{
                phone.sendSms();
            },"A").start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()->{
                phone2.call();
            },"B").start();
        }
    }
    //Phone3唯一的一个Class对象
    class Phone4{
    
        //静态同步方法,锁的是class
        public static synchronized void sendSms(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
    
        }
    
        //普通同步方法,锁的是调用者
        public  synchronized void call(){
            System.out.println("打电话");
        }
    
    }
    

    6.集合类不安全

    list不安全

    package com.mjh.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<>();//JUC的解决方案
    
            CopyOnWrite  写入时复制  简称COW  计算机程序设计领域的一种优化策略
            多个线程调用的时候,list,读取的时候是固定的,写入(覆盖)
            在写入的时候避免覆盖  造成数据问题
    
            Vector 和  CopyOnWriteArrayList<>
            Vector 的底层源码的add()用的是synchronized 效率相对较低
            CopyOnWriteArrayList<>底层源码的add()用的是Lock锁 效率高
             */
            List<String> list = new CopyOnWriteArrayList<>();
            for (int i = 0; i <10 ; i++) {
                new Thread(()->{
                    list.add(UUID.randomUUID().toString().substring(0,5));
                    System.out.println(list);
                },String.valueOf(i)).start();
            }
    
        }
    }
    

    set 不安全

    package com.mjh.unsafe;
    
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.UUID;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    //同理可证  java.util.ConcurrentModificationException
    public class SetTest {
        public static void main(String[] args) {
            /*多线程下的 HashSet 不安全
            1.工具类
                Set<String> set= Collections.synchronizedSet(new HashSet<>());
            2.  Set<String> set= new CopyOnWriteArraySet<>();
             */
            Set<String> set= new CopyOnWriteArraySet<>();
            for (int i = 0; i <10 ; i++) {
                new Thread(()->{
                    set.add(UUID.randomUUID().toString().substring(0,4));
                    System.out.println(set);
                },String.valueOf(i)).start();
            }
        }
    }
    

    map不安全

    package com.mjh.unsafe;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    import java.util.concurrent.ConcurrentHashMap;
    
    public class MapTest {
    // 同理可证:java.util.ConcurrentModificationException
        public static void main(String[] args) {
           // Map<String,String> map = new HashMap<>();
            Map<String,String> map = new ConcurrentHashMap<>();
            for (int i = 0; i < 30; i++) {
                new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
                    System.out.println(map);
                },String.valueOf(i)).start();
            }
    
        }
    }
    

    7.常用的辅助类(必会)

    CountDownLatch

    package com.mjh.add;
    
    import java.util.concurrent.CountDownLatch;
    
    public class CountDownLatchDemo {
        //减法计数器
        public static void main(String[] args) throws InterruptedException {
            //总数是6,使用场景:必须要执行任务的时候,再使用
            CountDownLatch countDownLatch = new CountDownLatch(6);
    
            for (int i = 1; i <=6 ; i++) {
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+" go out");
                    countDownLatch.countDown();//数量-1
                },String.valueOf(i)).start();
            }
    
            countDownLatch.await();//等待计数器归零,然后再向下执行
    
            System.out.println("close  door");
        }
    }
    

    原理:

    countDownLatch.countDown();//数量-1

    countDownLatch.await();//等待计数器归零,然后再向下执行

    每次有线程调用countDown()数量-1,假设计数器变为0, countDownLatch.await();就会被唤醒,继续执行!

    CyclicBarrier

    package com.mjh.add;
    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class CyclicBarrierDemo {
        //加法计数器
        public static void main(String[] args) {
            /*
            集齐7颗龙珠召唤神龙
             */
            //召唤龙珠的线程
            CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
                System.out.println("神龙召唤成功!");
            });
    
            for (int i = 1; i <=7 ; i++) {
                final int temp=i;
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"收集"+temp+"个龙珠");
    
                    try {
                        cyclicBarrier.await();//等待召集7颗龙珠
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                },String.valueOf(i)).start();
            }
        }
    }
    
    

    Semaphore

    package com.mjh.add;
    
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    public class SemaphoreDemo {
        public static void main(String[] args) {
            //线程数量:停车位!这个Semaphore一般用于限流
            Semaphore semaphore = new Semaphore(3);//信号量就只有三个,超过三个线程进来其余就只有等待
    
            for (int i = 1; i <=6 ; i++) {
                new Thread(()->{
                    //acquire()  得到
                    try {
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName()+" 找到停车位");
                        TimeUnit.SECONDS.sleep(3);//找到之后让它停一会儿
                        System.out.println(Thread.currentThread().getName()+" 离开停车位");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        //release()  释放
                        semaphore.release();
                    }
                },String.valueOf(i)).start();
            }
        }
    
    }
    

    原理:

    ​ semaphore.acquire();获得,假设如果已经满了,等待,等待被释放为止!

    ​ semaphore.release();释放,会将当前的信号量释放,然后唤醒等待的线程!

    作用:多个线程资源互斥的使用!并发限流,控制最大的线程数!

  • 相关阅读:
    定位中方向余弦矩阵(DCM)简介
    前端UI框架小汇总
    前端知识点小结
    overflow兼容iOS
    使用Flexible实现手淘H5页面的终端适配
    获取当前Javascript脚本文件的路径
    Javascript中document.execCommand()的用法 ( 实现浏览器菜单的很多功能 )
    jquery中的map()方法与js中的map()方法
    js判断是否为移动端
    页面制作注意事项
  • 原文地址:https://www.cnblogs.com/mjjh/p/13708391.html
Copyright © 2011-2022 走看看