JUC系列——JUC入门及多线程并发


前言

随着JAVA程序员越来越卷,导致现在他们需要不断的学习更深的知识来达到卷的资本。本期系统教程主要介绍JUC,这一章就让我们从头来认识一下它吧,做为一名资深的JAVA程序员,你必需知道什么是JUC,JUC是用来做什么的,怎么用。这也是大厂面试必问技术之一。


一、JUC简介

1、出自哪位大神之手?

  • JUC 是美国的一位大学教师 Doug Lea (道格·利)设计并开发出来的。
  • Doug Lea 服务于纽约州立大学Oswego分校计算机学系的教师。
  • Doug Lea 是 JCP(JAVA社区项目)中的一员。
  • 让我们瞻仰一下大神的容貌,多向大神学习。
    在这里插入图片描述

2、JUC是什么?

  • JUC是 JDK中java.util.concurrent 包的简称(中文简称:并发工具包),它包含三个包:
    • java.util.concurrent
    • java.util.concurrent.atomic
    • java.util.concurrent.locks
  • 2004年10月发布的JDK1.5新增用来编写并发相关的基础api。
  • 目前JUC泛指JAVA多线程开发技术。

二、JUC主要解决什么问题?

JUC 主要解决的问题是:在高内聚低耦合的前提下,线程如何操作 资源类可以达到高并发高可用的效果?

先来学习一下几个基本概念

1、进程 、线程

  • 进程:
    • 操作系统可调配的最小单元。
    • 有自己独立的空间。
    • 比如:系统中运行的QQ、CF、王者,它们都是独立的进程。
  • 线程:
    • 线程是进程可中可调配的最小单元。
    • 一个进程内可以包含多个线程。
    • 如:360卫士里包含了许多功能,杀毒、下载软件等,它们是一个进程内不同的线程。

2、并发、并行

  • 并发:
    • 同一个功能被同时调用。
    • 同一段程序被同时调用。
  • 并行:
    • 不同的功能被同时调用。
    • 不同的程序被同时调用。

JUC就是解决多线程执行中遇到的问题,所以我们再来看一下JAVA里是怎么创建线程和操作线程的

3、JAVA创建线程的方式(模拟三种线程实现方式)

模拟一个多线程的需求:火车售票员 A、B、C三个人同时出售一列火车的10张票,售完为止。

1)继承Thread类(线程的初级用法)

使用JUC中的Lock实现不现线程对同一资源的操作
售票员类:

package com.juc.study;

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

/**
 * 火车售票员
 */
public class TrainConductor extends Thread {
    
    

    public TrainConductor() {
    
    
    }

    public TrainConductor(String name) {
    
    
        super(name);
    }

    /**
     * 售票
     */
    public void saleTickets() {
    
    
        // JUC中的锁,是轻量级的锁,实现类是可重入锁,
        // 意味着只要有人释放锁,另外一个人就可以重新拿到锁并执行。
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
    
    
            if(Ticket.nums > 0) {
    
    
                System.out.println(Thread.currentThread().getName() + "\t 卖出第:" + (Ticket.nums--) + "张,\t 还剩下:" + Ticket.nums);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }

    @Override
    public void run() {
    
    
        for(int i = 70; i > 0; i--) {
    
    
            saleTickets();
        }
    }
}

火车票类:

package com.juc.study;

public class Ticket {
    
    

    public static int nums = 10;

}

执行类:

import com.juc.study.Ticket;
import com.juc.study.TrainConductor;

public class TestJUC {
    
    


    public static void main(String[] args) {
    
    
        // 新建三个售票员
        TrainConductor trainConductorA = new TrainConductor("A");
        TrainConductor trainConductorB = new TrainConductor("B");
        TrainConductor trainConductorC = new TrainConductor("C");

        // 开始售票
        trainConductorA.start();
        trainConductorB.start();
        trainConductorC.start();
    }

}

执行结果:

A	 卖出第:9张,	 还剩下:8
C	 卖出第:8张,	 还剩下:7
B	 卖出第:10张,	 还剩下:8
C	 卖出第:6张,	 还剩下:5
C	 卖出第:4张,	 还剩下:3
A	 卖出第:7张,	 还剩下:6
C	 卖出第:3张,	 还剩下:2
C	 卖出第:1张,	 还剩下:0
A	 卖出第:2张,	 还剩下:1
B	 卖出第:5张,	 还剩下:4

2)实现Runnable 接口(线程的初级用法)

与继承Thead类的写法有点区别,大家注意观察
售票员类:

package com.juc.study;

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

/**
 * 售票员类
 */
public class TrainConductorRunnable implements Runnable{
    
    

    /**
     * 售票
     */
    public void saleTickets() {
    
    
        // JUC中的锁,是轻量级的锁,实现类是可重入锁,
        // 意味着只要有人释放锁,另外一个人就可以重新拿到锁并执行。
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
    
    
            if(Ticket.nums > 0) {
    
    
                System.out.println(Thread.currentThread().getName() + "\t 卖出第:" + (Ticket.nums--) + "张,\t 还剩下:" + Ticket.nums);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }

    @Override
    public void run() {
    
    
        for(int i = 70; i > 0; i--) {
    
    
            saleTickets();
        }
    }
}

火车票类(无变化):

package com.juc.study;

public class Ticket {
    
    

    public static int nums = 10;

}

执行类:

import com.juc.study.Ticket;
import com.juc.study.TrainConductor;
import com.juc.study.TrainConductorRunnable;

public class TestJUC {
    
    


    public static void main(String[] args) {
    
    
        // 新建三个售票员
        TrainConductorRunnable trainConductorA = new TrainConductorRunnable();
        TrainConductorRunnable trainConductorB = new TrainConductorRunnable();
        TrainConductorRunnable trainConductorC = new TrainConductorRunnable();

        // 新建三个线程
        Thread t1 = new Thread(trainConductorA, "A");
        Thread t2 = new Thread(trainConductorB, "B");
        Thread t3 = new Thread(trainConductorC, "C");

        // 开始售票
        t1.start();
        t2.start();
        t3.start();
    }

}

执行结果:

A	 卖出第:9张,	 还剩下:7
C	 卖出第:8张,	 还剩下:7
A	 卖出第:7张,	 还剩下:6
B	 卖出第:10张,	 还剩下:7
A	 卖出第:5张,	 还剩下:4
C	 卖出第:6张,	 还剩下:5
A	 卖出第:3张,	 还剩下:2
B	 卖出第:4张,	 还剩下:3
A	 卖出第:1张,	 还剩下:0
C	 卖出第:2张,	 还剩下:1

3)内部类方式(线程的中级用法)

使用内部类实现是不需要继承Thread类或实现Runnable接口,大家仔细看

火车售票员类:

package com.juc.study;

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

/**
 * 火车售票员(采用内部类的形式实现)
 */
public class TrainConductorInner {
    
    

    /**
     * 售票
     */
    public void saleTickets() {
    
    
        // JUC中的锁,是轻量级的锁,实现类是可重入锁,
        // 意味着只要有人释放锁,另外一个人就可以重新拿到锁并执行。
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
    
    
            if(Ticket.nums > 0) {
    
    
                System.out.println(Thread.currentThread().getName() + "\t 卖出第:" + (Ticket.nums--) + "张,\t 还剩下:" + Ticket.nums);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }

}

火车票类(无变化):

package com.juc.study;

public class Ticket {
    
    

    public static int nums = 10;

}

执行类:

import com.juc.study.Ticket;
import com.juc.study.TrainConductorInner;

public class TestJUC {
    
    

    public static void main(String[] args) {
    
    
    
        Thread t1 = new Thread("A") {
    
    
            @Override
            public void run() {
    
    
                for(int i = 70; i > 0; i--) {
    
    
                    TrainConductorInner a = new TrainConductorInner();
                    a.saleTickets();
                }
            }
        };
        t1.start();

        Thread t2 = new Thread("B") {
    
    
            @Override
            public void run() {
    
    
                for(int i = 70; i > 0; i--) {
    
    
                    TrainConductorInner b = new TrainConductorInner();
                    b.saleTickets();
                }
            }
        };
        t2.start();

        Thread t3 = new Thread("C") {
    
    
            @Override
            public void run() {
    
    
                for(int i = 70; i > 0; i--) {
    
    
                    TrainConductorInner c = new TrainConductorInner();
                    c.saleTickets();
                }
            }
        };
        t3.start();
    }

}

4) 使用lamada 表达式实现(高级用法,也是企业中最常使用的方法)

lamada表达式写完之后有点长,如果看不懂可以给它格式化一下

火车售票员类(仍然使用内部类,没变化):

package com.juc.study;

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

/**
 * 火车售票员(采用内部类的形式实现)
 */
public class TrainConductorInner {
    
    

    /**
     * 售票
     */
    public void saleTickets() {
    
    
        // JUC中的锁,是轻量级的锁,实现类是可重入锁,
        // 意味着只要有人释放锁,另外一个人就可以重新拿到锁并执行。
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
    
    
            if(Ticket.nums > 0) {
    
    
                System.out.println(Thread.currentThread().getName() + "\t 卖出第:" + (Ticket.nums--) + "张,\t 还剩下:" + Ticket.nums);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }

}

火车票类(没变化):

package com.juc.study;

public class Ticket {
    
    

    public static int nums = 10;

}

执行类(这里使用lamada表达式):

import com.juc.study.Ticket;
import com.juc.study.TrainConductorInner;

public class TestJUC {
    
    

    public static void main(String[] args) {
    
    
        new Thread(()->{
    
    for(int i = 70; i > 0; i--){
    
    TrainConductorInner a = new TrainConductorInner(); a.saleTickets();}}, "A").start();
        new Thread(()->{
    
    for(int i = 70; i > 0; i--){
    
    TrainConductorInner b = new TrainConductorInner(); b.saleTickets();}}, "B").start();
        new Thread(()->{
    
    for(int i = 70; i > 0; i--){
    
    TrainConductorInner c = new TrainConductorInner(); c.saleTickets();}}, "C").start();
    }

}


总结

最重要的一句话:在高内聚低耦合的前提下,线程操作资源类达到高并发高可用的效果就是JUC的任务。
JUC的入门就到这里,到这里大家应该会对JUC有点感觉了,感觉是不是挺好玩的,后面的章节更精彩,敬请期待。。。。。。

猜你喜欢

转载自blog.csdn.net/yezhijing/article/details/128080409