栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JUC高并发编程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JUC高并发编程

JUC概述 1.1 JUC简介
  • java.util.concurrent包名的简写,是关于并发编程的API
  • 与JUC相关的有三个包:java.util.concurrent、java.util.concurrent.atomic、java.util.concurrent.locks
1.2 进程和线程
  • 进程:系统中一个正在运行的应用程序;一个程序就是一个进程;进程操作系统资源分配的最小单位
  • 线程:系统分配处理器时间资源的基本单位;进程之内独立执行的一个单元执行流。线程是程序执行的最小单位
1.3 线程的状态 1.3.1 线程状态枚举 1.3.2 wait、sleep区别
  • sleep是Thread的静态方法。wait是Object的方法,任何对象实例都能调用。
  • sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。
  • 都可以呗interrupted方法中断。
1.4 并发和并行 1.4.1 串行模式
  • 所有任务都按先后顺序执行。一次只能取得一个任务,执行完成这个任务才能执行下一个任务。
1.4.2 并行模式
  • 多个任务同时进行。可以同时取得多个任务,并同时去执行这些任务。
1.4.3 并发
  • 并发:同一时刻多个线程在访问同一个资源,多个线程对一个点。例如:春运抢票 电商秒杀
  • 并行:多项工作一起执行,之后再汇总。例如:泡方便面,电水壶烧水,一边撕调料倒入桶中
1.4.5 小结(重点) 1.5 管程
  • 管理共享变量以及对其操作过程,让它们支持并发访问。也就是管理类的成员变量和成员方法,让这个类是线程安全的。
1.6 用户线程和守护线程
  • 用户线程(自定义线程):主线程结束了,用户线程还在执行,JVM存活,可以通过线程的setDaemon(true)方法设置线程为守护线程
  • 守护线程(垃圾回收):没有用户线程了,都是守护线程,JVM结束
2. Lock接口 2.1 Synchronized关键字 2.1.1 synchronized概述
synchronized{
	...
}

是java中的关键字,是一种同步锁

  1. 修饰代码块,被修饰的代码块为同步代码块,作用范围为{}内,作用对象是调用这个代码块的对象
  2. 修饰方法,被修饰的方法为同步方法,作用范围为整个方法,作用对象是调用这个方法的对象
  • 虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,故synchronized关键字不能被继承。如果在父类的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,那么子类中的这个方法默认情况下是不同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。
  • 当然,还可以在子类中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类同步方法,因此,子类到方法也相当于同步了
  1. 修饰静态方法,作用范围是整个静态方法,作用对象是这个类的所有对象
  2. 修饰类,作用范围是synchronized后面括号起来的部分,作用的对象是这个类的所有对象
2.1.2 模拟卖票
class Ticket {
    // 票数
    private int number = 100;

    // 卖票
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出第:" + (number--) + " 剩下:" + number);
        }
    }

}

public class TicketMain {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i<100; i++){
                    ticket.sale();
                }
            }
        }, "AA").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i<100; i++){
                    ticket.sale();
                }
            }
        }, "BB").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i<100; i++){
                    ticket.sale();
                }
            }
        }, "CC").start();
    }
}
2.1.3 多线程编程步骤(上)
  1. 创建资源类,创建属性和操作方法
  2. 创建多线程调用资源类的方法
2.2 Lock接口
  • 属于java.util.concurrent.locks包下,为锁和等待条件提供一个框架的接口和类,它不同与内置同步和监视器
  • Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作
2.2.1 synchronized和Lock的区别
  • synchronized是Java的关键字。在JVM层面上,而Lock是一个类
  • synchronized发生异常会自动释放锁,因此不会导致死锁的现象发生。Lock发生异常需要主动释放锁,否则会发生死锁。
  • synchronized不能够响应中断,等待的线程会一直等待。Lock可以让等待锁的线程响应中断。
  • synchronized不能获取锁的状态。Lock可以知道是否成功获取锁。
  • Lock可以提高多个线程进行读操作的效率。

竞争资源不激烈两者性能相当,而竞争资源激烈时(大量线程同时竞争),Lock性能远远优于synchronized。

Lock锁模拟卖票
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Ticket {
    // 票数
    private int number = 100;

    Lock lock = new ReentrantLock();

    // 卖票
    public synchronized void sale() {
        try{
            // 获取锁
            lock.lock();
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出第:" + (number--) + " 剩下:" + number);
            }
        }finally {
            // 释放锁
            lock.unlock();
        }
    }

}

public class LTicketMain {

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

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

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

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

细节

  • 线程调用start()方法不一定会马上创建,具体什么时候创建由操作系统决定。操作系统空闲时会立马创建线程,不空闲会等一会创建线程。
2.3 创建线程的四种方式
  • 继承Thread类
  • 实现Runnable接口
  • 使用Callable接口
  • 是一个线程池
3. 线程间通信

初始化变量i=0,i=0时线程A对i+1,i !=0,线程B对i-1

synchronized
public class Share {
    private int i = 0;

    // 加一
    public synchronized void incr() throws InterruptedException {
        while(i != 0){
            this.wait();
        }
        i++;
        System.out.println(Thread.currentThread().getName() + "::"+ i);
        this.notifyAll();

    }

    // 减一
    public synchronized void decr() throws InterruptedException {
        while(i == 0){
            this.wait();
        }
        i--;
        System.out.println(Thread.currentThread().getName() + "::"+ i);
        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.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
    }
}
Lock
public class Share {
    private int i = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    // 加一
    public void incr() throws InterruptedException {
        lock.lock();
        try{
            while (i != 0) {
                condition.await();
            }
            i++;
            System.out.println(Thread.currentThread().getName() + "::" + i);
            condition.signalAll();
        }finally {
            lock.unlock();
        }

    }

    // 减一
    public void decr() throws InterruptedException {
        lock.lock();
        try{
            while (i == 0) {
                condition.await();
            }
            i--;
            System.out.println(Thread.currentThread().getName() + "::" + i);
            condition.signalAll();
        }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();

    }
}

注意: 当while条件换成if条件会造成虚假唤醒的情况,i会出现负数

虚假唤醒导致if在多线程环境下出错,因为它不再判断条件是否满足,继续从wait()方法之后执行。而while还会再次判断条件是否满足,如果不满足就不会执行。

4. 线程间定制化通信

让线程按照指定顺序执行

5. 集合的线程安全 5.1 集合线程不安全演示 5.2 ArrayList解决方案 Vector
List list= new Vector<>();
  • 在所有方法上都加上了synchronized关键字实现线程安全
Collections
List list= Collections.synchronizedList(new ArrayList<>());
CopyonWriteArrayList
List list = new CopyOnWriteArrayList<>();

底层原理

  • 并发读,独立写:读的时候是读原数组,写的时候会copy新的数组,在新的数组上进行操作,然后和原数组合并,再次读会读到合并后的数组。
5.3 HashSet线程不安全
  • 使用CopyOnWriteArraySet
5.4 HashMap线程不安全问题
  • ConcurrentHashMap
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/294516.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号