阅读 481
 JUC学习笔记(一)

JUC学习笔记(一)

一、1、JUC是什么

1. juc(java.util.concurrent)

是java.util.concurrent在并发编程中使用的工具类,jdk1.5开始出现的。 image.png

2. 进程、线程回顾

进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。 (简单理解就是电脑上的运行程序,如360杀毒软件、QQ音乐、chrome浏览器等)

image.png

线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。(相当于360杀毒软件既可以清理垃圾、又可以扫描病毒。清理垃圾操作可以看做是一个线程在工作、扫描病毒也是一个线程在工作)

image.png

3. 线程状态

Thread.State

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW, //新建

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE, //准备就绪

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED, //阻塞(线程等待执行)

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING, //不见不散(线程等待唤醒)

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING, //过时不候(超过等待唤醒时间)

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED; //终结
    }
复制代码

4. wait、sleep的区别

wait:释放锁等待(放开手去睡)

sleep:不释放锁等待(握紧手去睡,醒了手里还有锁)

5. 并发和并行的区别

并发:同一时刻多个线程在访问同一个资源,多个线程对一个点操作。比如:春运抢票、电商秒杀

并行:多项工作一起执行,之后再汇总。比如:泡方便面,可以一边撕开方便面包装加调料包,一边烧开水

6. 多线程使用技巧

【多线程编程的企业级套路+模板】

线程 -->操作(对外暴露的调用方法)-->资源类

二、Lock接口

1. Lock是什么

Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition 。 image.png

2. Lock接口的实现

ReentrantLock可重入锁

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...
 
   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }
复制代码
  • synchronized与Lock的区别

1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;

2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;

3.synchronized会自动释放锁(线程执行完同步代码会释放锁 ;线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;

4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)

6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

3. 卖票案例

(1)使用synchronized方案

/**
 * 资源类
 */
class Ticket{

    //总共有30张票
    private int number = 30;

    //对外暴露的调用的方法
    public synchronized void saleTicket(){

       if(number > 0){
           System.out.println(Thread.currentThread().getName() + "\t卖出第:" + (number--) + "\t还剩下:" + number);
       }

    }
}

/**
 *题目:三个售票员   卖出   30张票
 * 【多线程编程的企业级套路+模板】
 * 1.在高内聚低耦合的前提下,线程    操作(对外暴露的调用方法)     资源类
 */
public class SaleTicket {

    public static void main(String[] args) {

        //资源类
        Ticket ticket = new Ticket();

        //A售票员
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <=40; i++) {
                    ticket.saleTicket();
                }
            }
        }, "A").start();

        //B售票员
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <= 40; i++) {
                    ticket.saleTicket();
                }
            }
        }, "B").start();

        //C售票员
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <=40; i++) {
                    ticket.saleTicket();
                }
            }
        }, "C").start();

    }
复制代码

(2)使用juc中lock锁方案

/**
 * 资源类
 */
class TicketLock{

    private int number = 30;

    Lock lock  = new ReentrantLock();

    //对外暴露的调用方法
    public void saleTicket(){
        try {
            lock.lock();//对需要加锁的加锁处理
            System.out.println(Thread.currentThread().getName() + "\t卖出第:" + (number--) + "\t还剩下:" + number);
        } finally {
            lock.unlock(); //释放锁
        }
    }
}

/**
 *题目:三个售票员   卖出   30张票
 * 【多线程编程的企业级套路+模板】
 * 1.在高内聚低耦合的前提下,线程    操作(对外暴露的调用方法)     资源类
 */
public class SaleTicket {

    public static void main(String[] args) {

        /********使用juc中lock锁方式********/
        Ticket ticket = new Ticket();
        new Thread(() -> {for (int i = 0; i <= 40; i++) ticket.saleTicket();}, "A").start();
        new Thread(() -> {for (int i = 0; i <= 40; i++) ticket.saleTicket();}, "B").start();
        new Thread(() -> {for (int i = 0; i <= 40; i++) ticket.saleTicket();}, "C").start();


    }
复制代码

执行结果:

image.png

三、线程间通信

【多线程编程的企业级套路+模板】

1、判断 2、干活 3、通知

【多线程之间的虚假唤醒】

判断不能使用if,需要使用while

1. 生产者+消费者

(1) synchronizd、wait()、notifyAll()


/**
 * 资源类
 */
class AdditionAndSubtraction{

    private int number = 0;

    //number加1
    public synchronized void increment() throws InterruptedException {
        //判断
        while(number != 0){
            this.wait();
        }
        //干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        //通知
        this.notifyAll();
    }

    //number减1
    public synchronized void decrement() throws InterruptedException {
        //判断
        while(number == 0){
            this.wait();
        }
        //干活
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        //通知
        this.notifyAll();
    }

}


/**
 * 题目:现在两个线程,可以操作初始值为零的一个变量,
 * 实现一个线程对该变量加1,一个线程对该变量-1,
 * 实现交替,来10轮,变量初始值为0.
 *      1.高内聚低耦合前提下,线程操作资源类
 *      2.判断/干活/通知
 *      3.防止虚假唤醒(判断只能用while,不能用if)
 * 知识小总结:多线程编程套路+while判断+juc
 */
public class ProducersAndConsumersTest {

    public static void main(String[] args) {
        AdditionAndSubtraction additionAndSubtraction = new AdditionAndSubtraction();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        additionAndSubtraction.increment();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "A").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        additionAndSubtraction.decrement();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "B").start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        additionAndSubtraction.increment();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "C").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        additionAndSubtraction.decrement();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "D").start();
    }

}
复制代码

执行结果:

image.png

(2) JUC方式


class AdditionAndSubtraction2{

    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void increment() throws InterruptedException {
        try {
            lock.lock();
            //需要加锁的部分
            //1、判断
            while (number != 0){
                condition.await();
            }
            //2、干活
            number++;
            System.out.println(Thread.currentThread().getName() + "\t" +number);
            //3、通知
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void decrement() throws InterruptedException {

        try {
            lock.lock();
            //需要加锁的部分
            //1、判断
            while (number == 0){
                condition.await();
            }
            //2、干活
            number--;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            //3、通知
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}


/**
 * 题目:现在两个线程,可以操作初始值为零的一个变量,
 * 实现一个线程对该变量加1,一个线程对该变量-1,
 * 实现交替,来10轮,变量初始值为0.
 *      1.高内聚低耦合前提下,线程操作资源类
 *      2.判断/干活/通知
 *      3.防止虚假唤醒(判断只能用while,不能用if)
 *		4.标志位
 * 知识小总结:多线程编程套路+while判断+juc
 */
public class ProducersAndConsumersTest {

    public static void main(String[] args) {

        AdditionAndSubtraction2 additionAndSubtraction2 = new AdditionAndSubtraction2();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    additionAndSubtraction2.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    additionAndSubtraction2.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    additionAndSubtraction2.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();


        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    additionAndSubtraction2.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();


    }

}
复制代码

执行结果:

image.png

四、线程间定制化调用通信

package com.example.jucdemo.test;


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

class ShareResource{


    private int number = 1; //A:1,B:2,C:3,D:4
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();


    public void print5(){
        try {
            lock.lock();
            //1、判断
            while (number != 1){
                condition1.await();
            }
            //2、干活
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            //3、通知
            number = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print10(){
        try {
            lock.lock();
            //1、判断
            while (number != 2){
                condition2.await();
            }
            //2、干活
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            //3、通知
            number = 3;
            condition3.signal();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }


    public void printd15(){
        try {
            lock.lock();
            //1、判断
            while(number != 3){
                condition3.await();
            }
            //2、干活
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            //3、通知
            number = 1;
            condition1.signal();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

}


/**
 *
 * @Description:
 * 多线程之间按顺序调用,实现A->B->C
 * 三个线程启动,要求如下:
 *
 * AA打印5次,BB打印10次,CC打印15次
 * 接着
 * AA打印5次,BB打印10次,CC打印15次
 * ......来10轮
 *
 */
public class ThreadOrderAccess {

    public static void main(String[] args) {

        //资源类
        ShareResource shareResource = new ShareResource();

        //线程操作资源类
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareResource.print5();
            }
        }, "AA").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareResource.print10();
            }
        }, "BB").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareResource.printd15();
            }
        }, "CC").start();


    }

}
复制代码

执行结果:

image.png

小结

人生第一次文章献给了掘金,记录一下自己学习笔记和人生杂记

文章分类
后端
文章标签