JUC从入门到精通

目录

1 什么是JUC?

1.1 JUC简介

1.2 进程和线程的概念

1.3 线程的状态

1.3.1 线程的状态:

1.3.2 wait/sleep的区别

1.4 并发与并行

1.4.1 串行模式

1.4.2 并行模式

1.4.3 并发

1.4.4 并行

1.4.5 用户线程和守护线程

2 LOCK接口

2.1 synchronized

2.1.1 synchronized关键字

2.2.2 售票员买票

2.2 LOCK接口

2.3 Lock接口

2.4 synchronized与lock的差异


1 什么是JUC?

1.1 JUC简介

在Java中JUC就是java.uitl.concurrent工具包的简称。这是一个处理线程的工具包JDK1.5后出现的。

1.2 进程和线程的概念

进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是程序的实体。

线程:线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

1.3 线程的状态

1.3.1 线程的状态:

new(新建),Runnable(准备就绪),Blocked(阻塞),Waiting(等待),TimeWaiting(过时不候),Terminated(终结)

1.3.2 wait/sleep的区别

(1)sleep是Thread的静态方法,wait是Object的方法任何实例对象都能调用。

(2)sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。

(3)它们都可以被interrupt打断

1.4 并发与并行

1.4.1 串行模式

串行模式:表示所有的任务都按先后顺序执行。

1.4.2 并行模式

并行模式:同时取得多个任务,并可以同时执行这些所取得的任务。并行模式相当于一条长长的队列。

1.4.3 并发

并发:同一时刻多个线程访问一个资源,如:春运抢票,秒杀抢购

1.4.4 并行

并行:多项工作一起执行,之后再汇总。

管程:Monitor监视器(锁)

监视器:是一种同步机制,保证同一个时间内只有一个线程去访问被保护的数据或代码。

1.4.5 用户线程和守护线程

用户线程:自定义线程(主程序结束了,用户线程还在运行,JVM存活)

public class Main {
    public static void main(String[] args) {
        Thread a = new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().isDaemon());
            while (true){

            }
        },"a");
        a.start();
        System.out.println(Thread.currentThread().getName()+"over");
    }
}

守护线程:(没有用户线程,都是守护线程,JVM结束运行)

package com.workspace.main;

public class Main {
    public static void main(String[] args) {
        Thread a = new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().isDaemon());
            while (true){

            }
        },"a");
        a.setDaemon(true); //设置守护线程
        a.start();
        System.out.println(Thread.currentThread().getName()+"over");
    }
}

2 LOCK接口

2.1 synchronized

2.1.1 synchronized关键字

synchronized是Java中一个重要的关键字,是一种同步锁。

它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。


2.2.2 售票员买票

public class SaleTicket {
    //1.创建资源类 定义属性和操作方法
    static class Ticket{
        // 票数
        private int number = 30;
        public synchronized void sale(){
            //判断是否有票
            if (number > 0) {
                System.out.println(Thread.currentThread().getName()+"卖出,还剩"+ --number);
            }
        }
    }
    
    public static void main(String[] args) {
        // 创建ticket对象
        Ticket ticket = new Ticket();
        // 创建3个线程,分别售票
        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"A").start();;
        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"B").start();;
        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}

2.2 LOCK接口

为锁和等待条件提供一个框架的接口和类,不同于内置同步和监视器, LOCK是类,可通过类实现同步访问,多个接口实现类:可重入锁等

可重入锁的代码定义 :ReentrantLock

private final ReentrantLock lock = new ReentrantLock(true);

用lock实现卖票操作

public class LsaleTicket {
    //1.创建资源类 定义属性和操作方法
    static class LTicket{
        // 票数
        private int number = 30;
        // 创建可重入锁
        private final ReentrantLock lock = new ReentrantLock();
        // 买票方法
        public void sale(){
            // 上锁
            lock.lock();
            try{
                //判断是否有票
                if (number > 0) {
                    System.out.println(Thread.currentThread().getName()+"卖出,还剩"+ --number);
                }
            } finally{
                // 解锁
                lock.unlock();
            }
        }
    }
    
    public static void main(String[] args) {
        // 创建ticket对象
        LTicket ticket = new LTicket();
        // 创建3个线程,分别售票
        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"A").start();;
        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"B").start();;
        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}

2.3 Lock接口

public interface Lock {
	void lock();
	void lockInterruptibly() throws InterruptedException;
	boolean tryLock();
	boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
	void unlock();
	Condition newCondition();
}

2.4 synchronized与lock的差异

synchronized是java中内置关键字,而lock是一个类,可以实现同步访问且比synchronized中的方法更加丰富
synchronized不会手动释放锁,而lock需手动释放锁(不解锁会出现死锁,需要在 finally 块中释放锁)
lock等待锁的线程会相应中断,而synchronized不会相应,只会一直等待
通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到
当多个线程竞争的时候,Lock 可以提高多个线程进行读操作的效率

3 线程间的通信

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

线程间的通信具体步骤:

  1. 创建资源类,在资源类中创建属性和操作方法
  2. 在资源类操作方法:判断、操作、通知
  3. 创建多个线程,调用资源类的操作方法

3.1 synchronized案例

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

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

notify()、notifyAll()的区别
notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意性的。

notifyAll():唤醒在此对象监视器上等待的所有线程。

public class ThreadDemo1 {

    //  第一步创建资源类,定义属性和方法
    static class Share{
        // 初始值
        private int number = 0;

        // 数值+1方法
        public synchronized void incr() throws InterruptedException {
            // 第二步 判断如果不为0则等待
            if (number != 0) {
                this.wait();// 哪里沉睡,哪里醒来
            }
            // 数值为0,就+1
            number++;
            System.out.println(Thread.currentThread().getName()+"数值为:"+ number);
            // 通知其他线程
            this.notifyAll();
        }

        // 数值-1方法
        public synchronized void decr() throws InterruptedException {
            // 第二步 判断如果不为1则等待
            if (number != 1) {
                this.wait();// 哪里沉睡,哪里醒来
            }
            // 数值为1,就-1
            number--;
            System.out.println(Thread.currentThread().getName()+"数值为:"+ number);
            // 通知其他线程
            this.notifyAll();
        }
    }

    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for (int i = 0; i < 10 ; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

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

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

会出现虚拟唤醒导致

// 第二步 判断如果不为0则等待
if (number != 0) {
    this.wait(); // 哪里沉睡,哪里醒来
}

 解决方案把if变为while每次都要执行判断

while (number != 0) {
    this.wait(); //哪里沉睡,哪里醒来
}

执行结果

 3.2 Lock 案例

public class ThreadDemo2 {
    //  第一步创建资源类,定义属性和方法
    static class Share{

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

        // 数值+1方法
        public void incr() throws InterruptedException {
            // 上锁
            lock.lock();
            try {
                while(number != 0){
                    condition.await();
                }
                number++;
                System.out.println(Thread.currentThread().getName()+"数值为:"+ number);
                // 通知
                condition.signalAll(); // signalAll()唤醒所有等待线程;signal()唤醒一个等待线程随机
            } finally {
                lock.unlock();
            }
        }

        // 数值-1方法
        public void decr() throws InterruptedException {
            // 上锁
            lock.lock();
            try {
                while (number != 1){
                    condition.await();
                }
                number--;
                System.out.println(Thread.currentThread().getName()+"数值为:"+ number);
                // 通知
                condition.signalAll(); // signalAll()唤醒所有等待线程;signal()唤醒一个等待线程随机
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()-> {
            for (int i = 0; i < 10 ; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()-> {
            for (int i = 0; i < 10 ; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()-> {
            for (int i = 0; i < 10 ; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()-> {
            for (int i = 0; i < 10 ; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值