JUC并发编程

导读:本篇文章讲解 JUC并发编程,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

JUC并发编程

1:什么是JUC

在这里插入图片描述
java.util.concurrent在并发编程中使用的工具类

业务:普通的线程代码 Thread
Runnable 没有返回值、效率相比于Callable相对较低

2:线程和进程

1:线程和进程
进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。

例如: 使用 QQ ,查看进程一定有一个 QQ.exe 的进程,我可以用 qq 和 A 文字聊天,和 B 视频聊天,给 C 传文件,给 D 发一段语言, QQ 支持录入信息的搜索。

大四的时候写论文,用 word 写论文,同时用 QQ 音乐放音乐,同时用 QQ 聊天,多个进程。
word 如没有保存,停电关机,再通电后打开 word 可以恢复之前未保存的文档, word 也会检查你的拼写,两个线程:容灾备份,语法检查

一个进程往往包好多个线程,至少包含一个!
Java默认的有几个线程? 2个线程;main线程,GC线程

Java真的可以开线程吗? 不可以
因为:调用的底层的C++方法 (native本地的)

public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

private native void start0();

2:并发,并行;
并发:(多个线程同时操作一个资源)

  • CPU 一核,模拟出来多个线程。快速交替。

并行:(多个人一起行走)

  • CPU多核,多个线程可以同时进行。

线程有几种状态
6个状态:新生,运行,阻塞,等待,超时等待,死亡。

在这里插入图片描述

public enum State {
   
    NEW,
    RUNNABLE,

    BLOCKED,
    WAITING,

    TIMED_WAITING,
    TERMINATED;

}

Wait/Sleep的区别

1:来自于不同的类
wait ==> Object
Sleep ==>Thread

2:关于锁的释放;
Wait会释放锁,Sleep不会释放,抱着锁睡觉。

3:使用的范围不同
Wait必须在同步的代码块中
Sleep:可以在任何地方睡

4:是否需要捕获;
Wait 不需要捕获异常
Sleep 需要捕获异常;

3:Lock锁

1:传统的Synchronized

  • 什么是“锁”?
  • “锁”到底长啥样?
  • 它锁定的是线程代码还是其他什么东西?

https://www.zhihu.com/question/57794716

/**
 * 线程就是一个单独的资源类,没有任何的附属操作。
 * 1:属性、方法
 */
public class SaleTicketDemo01 {

    //并发:多个线程操作同一个类,把资源丢入线程;
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        //@FunctionalInterface函数式接口,,Lambda表达式(参数)->{  代码 }
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"C").start();
    }

}

//资源类:
class Ticket{

    //属性和方法;
    private int number = 50;

    //买票的方式
    //synchronized 的本质:排队:锁
    public synchronized void sale(){
        if (number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余:"+number);

        }
    }

}

2:Lock锁

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

公平锁:非常公平; 3h 3s

为什么要使用非公平锁:因为公平。( 3h 3s)
非公平锁:什么都不公平:可以插队(默认)

package com.baidu.demo01;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SaleTicketDemo02 {
    //并发:多个线程操作同一个类,把资源丢入线程;
    public static void main(String[] args) {
        Ticket2 ticket2 = new Ticket2();

        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket2.sale();
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket2.sale();
            }
        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket2.sale();
            }
        },"C").start();
    }
}

/**
 * Lock的三部曲;
 * 1:new ReentrantLock();
 * 2、加锁
 * 3:finall()  解锁
 *
 */
class Ticket2 {

    //属性和方法;
    private int number = 50;

    //Lock
    Lock lock = new ReentrantLock();

    public void sale() {

        //加锁
        lock.lock();


        try {

            //业务代码
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "票,剩余:" + number);

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }
}

4: Synchronized和Lock的区别

1:Synchronized是内置的Java对象。Lock是一个类。
2:Synchronized无法判断获取锁的状态。Lock可以判断是否获得了锁。
3:Synchronized会自动释放锁。Lock必须手动释放锁!如果的不释放,就会死锁
4:Synchronized线程1(获得锁,阻塞),线程2(等待,傻傻的等);Lock不会一直等待下去
5:Synchronized 可重入锁,不可中断,非公平。Lock,可重入锁,可以判断锁,非公平(可以自己设置)
6:Synchronized适合少量的代码同步问题,Lock,适合大量的代码同步问题。

5:生产者和消费者问题

package com.baidu.producer;

public class Box {
    public static void main(String[] args) {
        PublicBox publicBox = new PublicBox();

        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                publicBox.incrent();
            }
        },"t1").start();

        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                publicBox.decreace();
            }
        },"t2").start();
    }
}


class PublicBox {
    private int number = 0;

    //生产者
    public synchronized void incrent(){
        while (number == 5){
            //等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //业务代码;
        number++;
        System.out.println(Thread.currentThread().getName()+"苹果生成成功,快来消费");

        //通知;
        notifyAll();

    }

    //消费者
    public synchronized void decreace(){
        while (number == 0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        number--;
        System.out.println(Thread.currentThread().getName()+"消费苹果成功!");

        notifyAll();
    }
}

测试结果:
在这里插入图片描述

生产者和消费者问题Synchronized版

package com.baidu.producer;

/**
 * 线程之间的通讯问题:生产者和消费着问题!等待唤醒,通知唤醒。
 * 线程之间的交替执行,,A   B 同时操作一个变量。num = 0;
 * A num +1;
 * B num -1;
 */
public class A {

    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;

    //+1操作
    public synchronized void increment() throws InterruptedException {
        if (number!=0){ //0
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);

        //通知其他线程,我加一完毕了
        this.notifyAll();
    }

    //-1操作
    public synchronized void decrement() throws InterruptedException {
        if (number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);

        //通知其他线程,我减一完毕了
        this.notifyAll();
    }

}

上面运行的是两条线程在跑;如果4条那,8条那。是不是可以得出我们想要的结果那。

问题存在:A B C D 4条线程 同时进行,会出现虚假唤醒
在这里插入图片描述
原因是我们应该用while语句

JUC版的

通过Lock找到Condition
在这里插入图片描述
在这里插入图片描述
代码实现:

package com.baidu.producer;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class B {

    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 number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    
    //+1操作
    public void increment() throws InterruptedException {

        lock.lock();

        try {
            //业务代码
            while (number!=0){ //0
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);

            //通知其他线程,我加一完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    //-1操作
    public 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();
        }
    }
}

结果:
在这里插入图片描述

这样的结果不是我们想要的,我们想要的结果是ABC ABC ABC这种连续的结果,为不是乱序的;

Condition 精准的通知和唤醒线程

package com.baidu.producer;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class C {
    //让一下的三个线程精准的通知;
    //A 执行完调用B,B 执行完调用C,C 执行完调用A,

    public static void main(String[] args) {
        Data3 data = new Data3();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                data.printA();
            }

        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                data.printB();
            }

        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 5; 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

    public void printA(){

        //加锁
        lock.lock();
        try {
            //业务代码,判断等待---执行---通知;
            while (number!=1){
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>AAAAAA");

            //唤醒,唤醒指定的人;B
            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()+"=>BBBBBB");

            //唤醒,唤醒指定的人;C
            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()+"=>CCCCCC");
            //唤醒
            number = 1;
            condition1.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }

    }
}


测试结果:
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/71878.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!