当前位置: 首页 > article >正文

并发编程01:基础篇

1.1 线程基础

  • 1把锁:synchronized
  • 2个并:
    • 并行(concurrent):是在同一实体上的多个事件,是在一台机器上“同时”处理多个任务,同一时刻,其实是只有一个事情再发生。
    • 并发(parallel):是在不同实体上的多个事件,是在多台处理器上同时处理多个任务,同一时刻,大家都在做事情,你做你的,我做我的,各干各的。
  • 3个程:
    • 进程:在系统中运行的一个应用程序,每个进程都有它自己的内存空间和系统资源
    • 线程:也被称为轻量级进程,在同一个进程内会有1个或多个线程,是大多数操作系统进行时序调度的基本单元。
    • 管程:Monitor(锁),也就是我们平时所说的锁。Monitor其实是一种同步机制,它的义务是保证(同一时间)只有一个线程可以访问被保护的数据和代码,JVM中同步是基于进入和退出监视器(Monitor管程对象)来实现的,每个对象实例都会有一个Monitor对象,Monitor对象和Java对象一同创建并销毁,底层由C++语言实现。
  • 线程分类(一般不做特别说明配置,默认都是用户线程):
    • 用户线程:是系统的工作线程,它会完成这个程序需要完成的业务操作。
    • 守护线程:是一种特殊的线程为其他线程服务的,在后台默默地完成一些系统性的任务,比如垃圾回收线程就是最典型的例子。守护线程作为一个服务线程,没有服务对象就没有必要继续运行了,如果用户线程全部结束了,意味着程序需要完成的业务操作已经结束了,系统可以退出了。所以假如当系统只剩下守护线程的时候,守护线程伴随着JVM一同结束工作。
public class DaemonDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " 开始运行," + (Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));
            while (true) {

            }
        }, "t1");
        t1.setDaemon(true);//通过设置属性Daemon来设置当前线程是否为守护线程
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + " 主线程结束");
    }
}


//输出:t1 开始运行,守护线程
//main 主线程结束--->在main主线程结束后,守护线程会伴随着JVM一同结束工作,
//即使还有循环没有结束

1.2 JUC三大工具类

1.2.1 CountDownLatch(加计数器)

流程

  1. CountDownLatch countDownLatch = new CountDownLatch(8);
  2. countDownLatch.countDown(); 一个线程出来一个人,计数器就 -1
  3. countDownLatch.await(); 阻塞的等待计数器归零
  4. 执行后续步骤

样例

//减计数器
public class CountDownLatchDemo {
    //样例说明:8个同学离开教室后,班长关门
    public static void main(String[] args) {
        //创建CountDownLatch对象,设置初始值
        CountDownLatch countDownLatch=new CountDownLatch(8);
        for(int i=1;i<=7;i++){
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 号同学离开了教室");
                //计数-1
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        try {
            countDownLatch.await();//阻塞等待计数器归零
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("班长锁门");
    }
}

总结
CountDownLatch使用给定的计数进行初始化。 由于调用了countDown方法,每次-1, await方法会一直阻塞到当前计数达到零,然后释放所有等待线程,并且任何后续的await调用都会立即返回。 这是一种一次性现象——计数无法重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier 。

CountDownLatch一个有用属性是它不需要调用countDown线程在继续之前等待计数达到零,它只是阻止任何线程通过await,直到所有线程都可以通过。

1.2.2 CyclicBarrier(减计数器)

流程

  1. 创建CyclicBarrier对象
  2. CyclicBarrier cyclicBarrier = new CyclicBarrier(count, new MyRunnable());
  3. 编写业务代码
  4. cyclicBarrier.await(); //在线程里面等待阻塞,累加1,达到最大值count时,触发我们传入进去MyRunnable执行。

样例

//加计数器
public class CyclicBarrierDemo {
    //样例说明:抽奖10次后必中,即第11次必中奖
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier=new CyclicBarrier(10,()->{
            System.out.println("已经抽奖10次,下一次必中奖");
        });
        //10次抽奖
        for(int i=1;i<=10;i++){
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 抽奖一次");
                try {
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}

总结CyclicBarrier和CountDownLatch其实非常相似,CyclicBarrier表示加法,CountDownLatch表示减法。

区别还是有的:

CyclicBarrier只能够唤醒一个任务,CountDownLatch可以唤起多个任务。
CyclicBarrier可以重置,重新使用,但是CountDownLatch的值等于0时,就不可重复用了。

1.2.3 Semaphore(信号灯)

流程

  1. 创建信号灯
    Semaphore semaphore = new Semaphore(6); // 6个位置
  2. 等待获取信号灯
    semaphore.acquire();//等待获取许可证
  3. 业务代码
  4. 释放信号
    semaphore.release();//释放资源

样例

//信号灯
public class SemaphoreDemo {
    //样例说明:6辆车,停到3个停车位
    public static void main(String[] args) {
        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();;
        }

    }
}

总结:在获得一个项目之前,每个线程必须从信号量中获得一个许可,以保证一个项目可供使用。 当线程完成该项目时,它会返回到池中,并且将许可返回给信号量,允许另一个线程获取该项目。


http://www.kler.cn/a/18129.html

相关文章:

  • 第8章 利用CSS制作导航菜单
  • PHP爬虫快速获取京东商品详情(代码示例)
  • 由播客转向个人定制的音频频道(1)平台搭建
  • 5G 现网信令参数学习(3) - RrcSetup(1)
  • 实现3D热力图
  • 机器视觉和计算机视觉的区别
  • Linux常用命令,你需要了解多少呢?
  • Java字符串的用法、原理、性能分析和总结
  • 杜甫经典长诗“三吏”“三别”赏析
  • FAST协议解析2 FIX Fast Tutorial翻译【PMap、copy操作符】
  • 代码随想录算法训练营day30 | 332. 重新安排行程,51. N 皇后,37. 解数独
  • Ubuntu22.04.2 LTS 安装nvidia显卡驱动及配置pytorch
  • David Silver Lecture 4: Model-Free Prediction
  • 【Java|golang】2432. 处理用时最长的那个任务的员工
  • ES堆内存:大小和交换
  • Mermaid流程图
  • .net7 通过 JsonTranscoding 实现 gRPC 与 Web API 一鱼两吃
  • 内网:定位域管理员
  • TokenGT:Transformer是强大的图学习器
  • java反序列化cc3链分析
  • docker基础命令
  • python基础实战7-字符串的format方法
  • 【观察】更懂业务的数智平台,才能应对数智化转型的“千变万化”
  • 5件关于JavaScript中this参数的事
  • 记录--极致舒适的Vue页面保活方案
  • linux内核:笔记1-内核和操作系统的关系