JUC并发编程回顾

1.什么是JUC

image

java.util工具包

业务: 普通的线程代码, 之前都是用的thread或者runnable接口

但是相比于callable来说,thread没有返回值,且效率没有callable高

image
image

2. 线程和进程

线程,进程

进程 : 一个运行中的程序的集合; 一个进程往往可以包含多个线程,至少包含一个线程

java默认有几个线程? 两个 main线程 gc线程

线程 : 线程(thread)是操作系统能够进行运算调度的最小单位。

对于java而言如何创建thread: 继承自thread,实现runnable接口,实现callable接口

Java真的可以开启线程吗? 开不了的,底层是用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 */
            }
        }
    }
    //本地方法,调用底层c++, java无法操作硬件
    private native void start0();

并发,并行

并发编程: 并发和并行

并发(多线程操作同一个资源,交替执行)

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

并行(多个人一起行走, 同时进行)

  • CPU多核,多个线程同时进行 ; 使用线程池操作


    image.png
public static void main(String[] args) {
        //获取CPU核数
        //CPU 密集型,IO密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }

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

所有的公司都很看重!

线程有几个状态? 6个状态

public enum State {
       // 新生
        NEW,

        // 运行
        RUNNABLE,

        // 阻塞
        BLOCKED,

        // 等待,死死的等
        WAITING,

        //超时等待
        TIMED_WAITING,

        //终止
        TERMINATED;
    }

wait/sleep的区

import java.util.concurrent.TimeUnit;
//企业一般用这个
 TimeUnit.SECONDS.sleep(2);
  1. 来自不同的类

    ​ wait来自object类, sleep来自线程类

  2. 关于锁的释放

    ​ wait会释放锁, sleep不会释放锁

  3. 使用的范围不同

    ​ wait必须在同步代码块中

    ​ sleep可以在任何地方睡

  4. 是否需要捕获异常

    ​ wait不需要捕获异常,只要是线程都会有中断异常。

    ​ sleep需要捕获异常

3. Lock锁(重点)

传统synchronized

本质: 队列和锁,放在方法上锁的是this,放在代码块中锁的是()里面的对象

/**
* 不推荐
 * 真正的多线程开发,公司中的开发
 * 线程就是一个单独的资源类没有任何附属的操作!
 * 1.属性、方法
 */

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

/**
 * 这样完全变成线程类,没有OOP,耦合性高!
 */
class MyThread implements Runnable{

    @Override
    public void run() {
        
    }
}

推荐这种

public class SaleTicketDemo01 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(() ->{
            for (int i = 2; i < 60; i++) {
                ticket.sale();
            }
            },"A").start();
        new Thread(() ->{
            for (int i = 2; i < 60; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(() ->{
            for (int i = 2; i < 60; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}

/**
 * 这样完全变成线程类,没有OOP,耦合性高!
 */
class Ticket{
    //属性、方法
    private int number = 50;
    int i = 1;

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

Lock 接口

image

实现类

image

reentrantLock构造器

    public ReentrantLock() {
        sync = new NonfairSync(); //无参默认非公平锁
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();//传参为true为公平锁
    }

公平锁: 十分公平: 先来后到,一定要排队

非公平锁: 十分不公平,可以插队(默认)

public class SaleTicketDemo {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        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 ticketNum = 30;
    private Lock lock = new ReentrantLock();

    public void sale() {
        lock.lock();
        try {
            if (this.ticketNum > 0) {
                System.out.println(Thread.currentThread().getName() + "购得第" + ticketNum-- + "张票, 剩余" + ticketNum + "张票");
            }
            //增加错误的发生几率
            Thread.sleep(10);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

synchronized和lock锁的区别

  1. synchronized内置的java关键字,Lock是一个java类
  2. synchronized无法判断获取锁的状态, Lock可以判断是否获取到了锁
  3. synchronized会自动释放锁,Lock必须要手动释放锁!如果不是释放锁,会产生死锁
  4. synchronized 线程1(获得锁,阻塞),线程2(等待); Lock锁就不一定会等待下去
  5. synchronized 可重入锁,不可以中断的,非公平的; Lock锁,可重入的,可以判断锁,非公平(可自己设置);
  6. synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码

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

4. 生产者和消费者问题

面试高频: 单例模式, 八大排序,生产者消费者,死锁

Synchronized实现 wait notify

package com.czp.syncconsumer;

public class Test {

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

        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.increment();
                    System.out.println(Thread.currentThread().getName() + "让Num增加, num => " + data.getNum());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.decrement();
                    System.out.println(Thread.currentThread().getName() + "让Num减少, num => " + data.getNum());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

    }
}

class Data {

    private int num = 0;

    public int getNum() {
        return num;
    }

    public synchronized void increment() throws InterruptedException {
        if (num != 0) {
            this.wait();
        }
        num++;
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        if (num == 0) {
            this.wait();
        }
        num--;
        this.notifyAll();
    }
}

问题存在,ABCD4个线程是否安全, 不安全 会有虚假唤醒

在这里插入图片描述

if判断改为while判断

因为if只会执行一次,执行完会接着向下执行if()外边的

而while不会,直到条件满足才会向下执行while()外边的

image.png

JUC版本生产者和消费者问题

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

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

Condition 精准的通知和唤醒线程


在这里插入图片描述

创建不同的监视器,达到顺序执行

image.png

你可能感兴趣的:(JUC并发编程回顾)