您当前的位置: 首页 > 

止步前行

暂无认证

  • 0浏览

    0关注

    247博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

JUC高并发学习笔记

止步前行 发布时间:2021-08-31 10:13:02 ,浏览量:0

1、什么是JUC

Java中,线程是一个重点,JUCjava.util.concurrent工具包的简称,这是一个处理线程的工具包,JDK 1.5开始出现。

1.1 进程与线程
  • 进程(Process)是系统进行资源分配和调度的基本单位,是操作系统结构的基础,进程是线程的容器。

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

小结:进程是系统资源分配的最小单位,线程是程序执行的最小单位。

1.2 wait()/sleep()的区别
  • sleep()Thread的静态方法,而wait()Object的方法,任何对象实例都能调用
  • sleep()不会释放锁,wait()会释放锁,但调用它的前提是当前线程占有锁,即代码要在synchronized
  • 它们都可以被interrupted方法中断
1.3 并发与并行 串行与并行
  • 串行表示所有任务都一一按先后顺序进行,串行是一次只能取得一个任务,然后执行这个任务

  • 并行意味着可以同时取得多个任务,并同时去执行所取得的这些任务。并行模式相当于将长长的一条队列,划分成了多条短队列,所以并行缩短了任务队列的长度。并行的效率从代码层次上强依赖于多进程/多线程代码,从硬件角度上则依赖于多核CPU。

并发

并发(concurrent)指的是多个程序可以同时运行的现象,并发描述的是多进程同时运行的现象。

要解决大并发问题,通常是将大任务分解成多个小任务,由于操作系统对进程的调度是随机的,所以切分成多个小任务后,可能会从任一小任务处执行,这可能会出现一些现象:

  • 可能出现一个小任务执行了多次,还没开始下个任务的情况。这时一般会采用队列或类似的数据结构来存放各个小任务的成果
  • 可能出现还没准备好第一步就执行第二步的可能。这时,一般采用多路复用或异步的方式,比如只有准备好产生了事件通知才执行某个任务
  • 可以多进程/多线程的方式并行执行这些小任务。也可以单进程/单线程执行这些小任务,这时很可能要配合多路复用才能达到较高的效率

并发:同一时刻多个线程在访问同一个资源,多个线程对一个点 并行:多项工作一起执行,之后再汇总

并发产生的条件:

  • 多线程
  • 共享变量
  • 原子操作

数据一致性:

  • 原子性
  • 一致性
  • 有序性:指令重排
1.4 用户线程和守护线程

用户线程:平时用到的普通线程,自定义线程 守护线程:运行在后台,是一种特殊的线程,比如垃圾回收 当主线程结束后,如果用户线程还在运行,那么JVM存活,如果没有用户线程,都是守护线程,则JVM结束

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

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

注:守护线程随着用户线程的结束而结束。可以在有较长耗时操作时,设置为守护线程进行。

2、Lock接口 2.1 synchronized

synchronizedJava中的关键字,是一种同步锁,它修饰的对象有以下几种:

  • 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象
  • 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象
  • 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象
  • 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象

虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。

如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步,而必须显式地在子类的这个方法中加上synchronized关键字才可以。

当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里取得锁的线程释放锁只会有两种情况:

  • 获取锁的线程执行完了该代码块,然后线程释放对锁的占有
  • 线程执行发生异常,此时JVM会让线程自动释放锁

那么如果这个取得锁的线程由于要等待IO或者其他原因(比如调用sleep()方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到

多线程编程步骤:

  • 第一步,创建资源类,在资源类创建属性和操作方法(多个线程抢占资源)

  • 第二步,创建多个线程,调用资源类的操作方法

场景:3个窗口卖30张票

//第一步  创建资源类,定义属性和和操作方法
class Ticket {
    //票数
    private int number = 30;
    //操作方法:卖票
    public synchronized void sale() {
        //判断:是否有票
        if(number > 0) {
            System.out.println(Thread.currentThread().getName()+" : 卖出:"+(number--)+" 剩下:"+number);
        }
    }
}

public class SaleTicket {
    //第二步 创建多个线程,调用资源类的操作方法
    public static void main(String[] args) {
        // 创建共享资源Ticket对象
        Ticket ticket = new Ticket();
        // 创建三个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                //调用卖票方法
                for (int i = 0; i             
关注
打赏
1657848381
查看更多评论
0.0485s