• 01-juc-入门概念


    01-juc-入门概念:

    1、JUC相关概念

    juc: java.util .concurrent工具包的简称。这是一个处理线程的工具包, JDK .1.5开始出现的。

    进程和线程

    进程:在面向线程设计的计算机结构中,进程是线程的容器。由多个线程组成。

    线程:是操作系统能够进行运算调度的最小单位。 它被包含在进程之中,是进程中的实际运作单位

    线程的状态:

    创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)

    查看源代码Thread.State

    public enum State {
    NEW,(新建)
    RUNNABLE,(准备就绪)
    BLOCKED,(阻塞)
    WAITING,(不见不散)
    TIMED_WAITING,(过时不候)
    TERMINATED;(终结) }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    wait和sleep的区别:类的区别和是否释放锁的区别。

    wait:是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程

    sleep:Thread 的静态方法,任何对象实例都能调用。不会释放锁,也不需要占用锁。让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。线程进入休眠,对象的机锁不释放。其他线程无法访问。

    并发和并行:

    串行:表示所有任务都一一按先后顺序进行

    并发:抢占cpu,多个任务交互执行,是同一时刻多个线程在访问同一个资源

    并行:同时取得多个任务,并同时去执行所取得的这些任务

    管程:
    管程在java中是锁,在操作系统中是monitor监视器,代表一种同步机制,同一时间内只能有一个线程访问且被保护数据

    比如jvm的同步基于进入和退出,是管程对象实现,每个对象都有一个monitor管程对象,都会随着java的对象进行创建和销毁
    管程对象对临界区加锁和解锁,大意就是进加锁,退是解锁,通过管程对象管理

    2、LOCK接口

    多线程编程步骤**(高内聚低耦合)**:第一步 创建资源类,创建属性和操作方法,第二步 创建多线程调用资源类的方法。

    2.1复习synchronized

    synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
    1.修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
    2.修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
    synchronized不属于方法的一部分,因此synchronized 关键字不能被继承。如果在父类中的某个方法使用了synchronized 关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。

    3.修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
    4.修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。

    package com.juc.sync;
    
    //1。创建一个资源类,定义属性和操作的方法
    class Ticket{
        //票数
        private int number=30;
        //操作的方法:买票
        public synchronized void sale(){
            //判断:是否有票
            if(number > 0) {
                System.out.println(Thread.currentThread().getName()+" : 卖出:"+(number--)+" 剩下:"+number);
            }
        }
    }
    //2.创建多个线程,调用资源类的操作方法
    public class SaleTick {
        public static void main(String[] args) {
            //创建ticket对象
            Ticket ticket = new Ticket();
            //创建线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<40;i++){
                        ticket.sale();
                    }
                }
            },"A1").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<40;i++){
                        ticket.sale();
                    }
                }
            },"A2").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<40;i++){
                        ticket.sale();
                    }
                }
            },"A3").start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
    1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
    2)线程执行发生异常,此时JVM会让线程自动释放锁。
    那么如果这个获取锁的线程由于要等待I0或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程只能等待。

    2.2LOCK接口

    可重入锁:类比厕所,可重用

    LOCK是类,可通过类实现同步访问,多个接口实现类:可重入锁等
    可重入锁的代码定义private final ReentrantLock lock = new ReentrantLock(true);上锁lock.lock();解锁lock.unlock();
    上锁与解锁中的代码如果出现异常,解锁会执行不了,所以最好加try…finally

    其他同synchronized方法。
    //卖票方法
        public void sale() {
            //上锁
            lock.lock();
            try {
                //判断是否有票
                if(number > 0) {
                    System.out.println(Thread.currentThread().getName()+" :卖出"+(number--)+" 剩余:"+number);
                }
            } finally {
                //解锁
                lock.unlock();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Lock与的Synchronized区别.

    Lock不是Java语言内置的,synchronized 是Java语言的关键字,是内置特性。Lock 是一个类,通过这个类可以实现同步访问;
    Lock和synchronized的不同, synchronized不需要手动释放锁,当synchronized 方法或者synchronized 代码块执行完之后,
    系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

    public interface Lock {
        //用来获取锁。如果锁已被其他线程获取,则进行等待。必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一
    般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。
        void lock();
        
        //
        void lockInterruptibly() throws InterruptedException;
        boolean tryLock();
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
        void unlock();
        
        
        //关键字synchronized与wait0/notify0可实现等待/通知模式,Lock的newContition()方法返回Condition对象,Condition 类
    也可以实现等待/通知模式。用notify()通知时, JVM会随机唤醒某个等待的线程,使用Condition类可以进行选择性通知,Condition 比较常用的两个方法: 
    await()会使当前线程等待,同时会释放锁,当其他线程调用signal()时,线程会重新获得锁并继续执行。
    signal()用于唤醒一个等待的线程。
        Condition newCondition();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    ReentrantLock 是唯一实现了 Lock 接口的类,并且 ReentrantLock 提供了更多的方法
    ReentrantLock可重入锁

    ReentrantReadWriteLock 里面提供了很多丰富的方法,不过最主要的有两个方法:readLock()和 writeLock()用来获取读锁和写锁
    writeLock();来获取读锁
    readLock();获取写锁

    假设有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁,反之同理

    3、线程间通讯

    线程间通信的模型有两种:共享内存和消息传递
    线程间的通信具体步骤:

    1. 创建资源类,在资源类中创建属性和操作方法
    2. 在资源类操作方法:判断、操作、通知
    3. 创建多个线程,调用资源类的操作方法
    4. 防止虚拟唤醒问题

    synchronized案例
    操作线程的时候,等待线程使用wait(),通知另外的线程操作用notify()、notifyAll()
    假设有两个线程,该线程在执行过程中,判断值(不是该值等待,让其他线程抢),操作值,通知另外一个线程的调度

    通过使用两个线程对0这个值操作,一个线程加1,一个线程减1,交替实现多次

    package com.juc.sync;
    
    //第一步,创建资源类,定义属性和操作方法
    class Share {
        //初始值
        private int number = 0;
    
        //第二步,干活,执行方法
        //+1的方法
        public synchronized void incr() throws InterruptedException {
            //第二步 判断 干活 通知
    //        if (number != 0) { //判断number值是否是0,如果不是0,等待
    //            this.wait(); //在哪里睡,就在哪里醒
    //        }
            while (number != 0) { //可以避免虚假唤醒
                this.wait();
            }
            //如果number值是0,就+1操作
            number++;
            System.out.println(Thread.currentThread().getName() + " :: " + number);
            //通知其他线程
            this.notifyAll();
        }
    
        //-1方法
        public synchronized void decr() throws InterruptedException {
            //判断
    //        if (number != 1) {
    //            this.wait();
    //        }
            while (number != 1) { //可以避免虚假唤醒
                this.wait();
            }
            //干活
            number--;
            System.out.println(Thread.currentThread().getName() + " :: " + number);
            //通知其他线程
            this.notifyAll();
        }
    }
    
    /**
     * 实现一个两个线程,一个线程对数据+1,另一个线程对数字-1.测试线程之间的通讯
     */
    public class ThreadDemo1 {
        //第三步:创建多个线程,调用资源类的操作方法
        public static void main(String[] args) {
            Share share = new Share();
            //创建线程
            new Thread(()->{
                for (int i=1;i<=10;i++){
                    try {
                        share.incr();
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            },"A1").start();//做加一操作
    
    
            //创建线程
            new Thread(()->{
                for (int i=1;i<=10;i++){
                    try {
                        share.decr();
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            },"A2").start();//做减一操作
    
    
    
            //多个线程产生的虚假唤醒情况,虚假唤醒是因为wait(); //在哪里睡,就在哪里醒,会跳过之前的if判断,
            //直接执行下面的代码。修改方式将if改为while
    
    
            new Thread(()->{
                for (int i=1;i<=10;i++){
                    try {
                        share.incr();
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            },"A3").start();//做加一操作
    
    
            //创建线程
            new Thread(()->{
                for (int i=1;i<=10;i++){
                    try {
                        share.decr();
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            },"A4").start();//做减一操作
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    if产生了虚拟唤醒:如果一个线程执行完毕后,通知其他线程,该线程又进入等待睡眠,可能会因为某些原因被唤醒后,if结构的语句就不会判断了,一直往下执行,所以需要将if换成while结构,每次都判断。因为wait在哪里睡眠就在哪里被唤醒,结果被某个异常唤醒了后回不去了,if结构不会在判断了,需要更改为while。

    lock实现以上案例

    private int number = 0;
        //创建lock
        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
        //+1方法
        public void incr(){
            //上锁
            lock.lock();
            try {
                //判断
                while (number!=0){
                    condition.await();
                }
                //执行干活
                number++;
                System.out.println(Thread.currentThread().getName() + " :: " + number);
                //通知其他线程
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //解锁
                lock.unlock();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    线程间的定制化通讯

    定制化通信即让线程进行一定的顺序操作

    案列:启动三个线程,按照如下要求:A1打印5此,A2打印10次,A3打印15次,一共进行10轮

    思路:
    通过标志位操作那个线程执行,是该标志位则执行操作,并且修改为下一个标志位,通知下一个标志位的线程

    package com.juc.lock;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 线程的自定义通讯,指定线程顺序执行和执行次数。案例来源尚硅谷
     */
    //第一步创建资源类
    class ShareResource{
        //属性,定义标志位
        private int flag=1;//1,A1线程,2,A2线程   3表示A3线程
        //创建Lock
        private Lock lock = new ReentrantLock();
        //创建三个condition
        private Condition condition1 = lock.newCondition();
        private Condition condition2 = lock.newCondition();
        private Condition condition3 = lock.newCondition();
    
        //打印5次的方法,参数第几轮
        public void print5(int loop){
            lock.lock();
            try {
                while (flag!=1){
                    condition1.await();
                }
                for (int i=1;i<=5;i++){
                    System.out.println(Thread.currentThread().getName()+"::"+i+":轮数:"+loop);
                }
                flag=2;//先修改标识位为2
                condition2.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public void print10(int loop){
            lock.lock();
            try {
                while (flag!=2){
                    condition2.await();
                }
                for (int i=1;i<=10;i++){
                    System.out.println(Thread.currentThread().getName()+"::"+i+":轮数:"+loop);
                }
                flag=3;//先修改标识位为3
                condition3.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public void print15(int loop){
            lock.lock();
            try {
                while (flag!=3){
                    condition3.await();
                }
                for (int i=1;i<=15;i++){
                    System.out.println(Thread.currentThread().getName()+"::"+i+":轮数:"+loop);
                }
                flag=1;//先修改标识位为2
                condition1.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    
    public class ThreadDemo3customcom {
        public static void main(String[] args) {
            ShareResource shareResource = new ShareResource();
            new Thread(()->{
                for(int i=1;i<=10;i++){
                    try {
                        shareResource.print5(i);
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            },"A1").start();
            new Thread(()->{
                for(int i=1;i<=10;i++){
                    try {
                        shareResource.print10(i);
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            },"A2").start();
            new Thread(()->{
                for(int i=1;i<=10;i++){
                    try {
                        shareResource.print15(i);
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            },"A3").start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
  • 相关阅读:
    计算机网络 划分子网 ICMP DHCP
    健康打卡每日提醒累了?那就让自动化帮你---HiFlow,应用连接自动化助手
    浅谈路由器基本结构与工作原理
    19 螺旋矩阵
    java基于Springboot+vue的房屋出租租房网站 elementui
    论文阅读:ECAPA-TDNN
    基于JAVA园艺生活网站计算机毕业设计源码+系统+数据库+lw文档+部署
    Java面试题及答案整理(2022最新版)
    SHRM在中国的认可度如何?这里说了实话
    [JAVA]排序
  • 原文地址:https://blog.csdn.net/qq_37171694/article/details/128139120