线程核心基础2

在学习的时候,一定要注意学习顺序,使用3W1H的方式进行学习:
What:是什么?
Who/When/Where : 应用场景 什么时候使用 在哪里使用
Why : 为什么要使用它
How: 内部原理

线程属性详解

属性名称用途
编号(ID)每个线程有自己的ID,用于标识不同的线程.被后续创建的线程使用,唯一性不能保证,不允许被修改.
名称(Name)作用让用户或程序员在开发、调试或运行过程中,更容易区分每个不同的线程、定位问题等.
是否是守护线程(isDaemon)true代表该线程是[守护线程],false代表线程是非守护线程[用户线程].守护线程是服务用户线程的,而JVM退出是看用户线程的,如果用户线程都运行完毕了守护线程还有没有运行完毕,这时候JVM退出不会管守护线程是否运行完毕,直接强制退出.
优先级(Priority)优先级这个属性的目的是告诉线程调度器,用户希望哪些线程相对多运行、哪些少运行.默认和父线程的优先级相等,共有10个等级,默认值是5.在实际编码过程中,不推荐修改优先级.

思考

  • 什么时候需要设置守护线程
  • 如何应用线程优先级来帮助程序运行?有哪些禁忌
  • 不同的操作系统如何处理优先级问题?
线程id

每个线程都有自己的一个id,这个id是不能修改的.main函数就是第一个线程,id是自增的.直接上代码

/**
 * ID从1开始,JVM运行起来后,自己创建的线程的ID早已不是0
 */
public class Id {
    public static void main(String[] args) {
        Thread thread = new Thread();
        //主线程ID:1 为什么不是0呢 先++ 变成1了
        System.out.println("主线程ID"+Thread.currentThread().getId());
        //?? 子线程ID9 为什么到了ID9了呢?
        System.out.println("子线程ID"+thread.getId());
    }
}

运行结果: 不知道大家有没有发现,主线的ID不是从0开始的,而且子线程的ID竟然变成了12 不是说好的ID是自增的吗?
在这里插入图片描述

不要慌,我们深入源码看一下,看到下面的代码,你肯定明白ID为什么不是从0开始的,因为它是先加1啊,所以主线程的ID就是1.

 /* For generating thread ID */
    private static long threadSeqNumber;
  private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

子线程ID为什么不是2,而是变成了其他的值呢? 我们DEBUG来看一下, 可以看到在启动程序的时候JVM早已经有多个thread初始化了.
1

有兴趣的可以执行查询了解一下

  • “Signal Dispatcher" 负责把操作系统的信号 发给适当的程序的
  • “Reference Handler” 和GC引用相关的线程
  • “Finalizer” 负责执行对象finalized的方法

也就是说当我们运行main()方法的时候,JVM不止创建了主线程还创建了很多辅助线程,这也就是说明我们创建的子线程的ID为什么变成了12,而不是从2开始.

线程名字

线程是如何命名的呢? 其实从Thread的构造方法中就可以看出来,我们打印出来的线程名称都是“Thread-XXX”对吧,其实还可以通过Thread的构造方法自定义线程的名称或者通过setName()方法自定义线程的名称.

  public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
   /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }
    public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }
守护线程

守护线程是干什么的?
守护线程就是给用户线程提供服务.
三个特性:
线程类型默认继承自父线程
被谁启动,通常所有的守护线程是由JVM启动
不影响JVM退出,JVM退出只看用户线程,不看守护线程.

守护线程和普通线程的区别

  • 整体无区别
  • 唯一区别在于JVM的离开
  • 守护线程是服务普通线程的[用户线程]

用户线程会影响JVM的退出,而守护线程不会影响JVM退出.JVM退出的时候不会关心守护线程是否关闭了,JVM退出的时候会强制关闭守护线程
一般在开发中是不需要把自己的线程设置为守护线程.

线程优先级

10个级别,默认是5.
:::tips
注意:程序设计不应依赖于优先级,因为不同的操作系统不一样,高度依赖操作系统.优先级会被操作系统改变.在实际编码过程中不推荐修改优先级.
:::

    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

未捕获异常如何处理

思考?

  • Java异常体系图
  • 在实际工作中,如何全局处理异常?为什么要全局处理?不处理会怎么样?

Java异常体系图 如下:
在这里插入图片描述

UncaugheException异常通过UncaughtExceptionHandler处理.
为什么需要UncaughtExceptionHandler?

  • 主线程可以轻松发现异常,子线程却不行,即便子线程抛出异常,但是主线程还是在正常运行,无法发现子线程出现异常

通过下面代码演示:

/**
 * 单线程:抛出 处理 有异常堆栈
 * 多线程:子线程发生异常 会有什么不同
 */
public class ExceptionInChildThread implements Runnable{

    public static void main(String[] args) {
        new Thread(new ExceptionInChildThread()).start();
        //对主线程而言 没有任何影响 子线程可以正常打印出异常
        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
        }
    }

    @Override
    public void run() {
        //线程抛出异常
        throw new RuntimeException();
    }
}

在这里插入图片描述

  • 子线程异常无法用传统的方法捕获

如下代码,通过try/catch包裹线程代码,无法捕捉到异常.

package com.prim.threadcoreknowledge.uncaughtexception;

/**
 * 1. 不加try catch抛出4个异常,都带线程名字
 * 2. 加了try catch 期望捕获到第一个线程的异常,线程234不应该运行,希望看到打印出Caught Exception
 * 3. 执行结果发现:根本没有Caught Exception 线程234依然运行并且抛出异常
 * <p>
 * 说明线程的异常不能用传统方法捕获
 */
public class CantCatchDirectly implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        try {
            new Thread(new CantCatchDirectly(), "MyThread-1").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "MyThread-2").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "MyThread-3").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "MyThread-4").start();
        } catch (RuntimeException e) {
            System.out.println("Caught Exception");
        }
    }

    @Override
    public void run() {
        throw new RuntimeException();
    }
}

在这里插入图片描述

  • 不能直接捕获的后果、提高健壮性

那么到底要如何捕捉子线程异常呢?

两种解决方案
  • (不推荐):手动在每个run方法里进行try/catch
  • (推荐):利用UncaughtExceptionHandler

如下UncaughtExceptionHandler是一个接口 通过uncaughtException() 方法可以拿到线程和异常.

    @FunctionalInterface
    public interface UncaughtExceptionHandler {
        /**
         * Method invoked when the given thread terminates due to the
         * given uncaught exception.
         * <p>Any exception thrown by this method will be ignored by the
         * Java Virtual Machine.
         * @param t the thread
         * @param e the exception
         */
        void uncaughtException(Thread t, Throwable e);
    }

下面看线程异常处理的调用策略 ThreadGroup

public
class ThreadGroup implements Thread.UncaughtExceptionHandler {    
public void uncaughtException(Thread t, Throwable e) {
    	//默认情况下parent是null
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            //调用Thread.setDefaultUncaughtExceptionHandler() 方法设置的
            //全局handler进行处理
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            //如果取到的不为null 就是使用我们实现的接口方法调用
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                //全局 handler也不存在就输出异常栈
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }
}

如何实现呢?

  • 给程序统一设置

通过调用Thread的静态方法setDefaultUncaughtExceptionHandler 可以给所有的线程进行设置异常捕捉

public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    private String name;

    public MyUncaughtExceptionHandler(String name) {
        this.name = name;
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        Logger anonymousLogger = Logger.getAnonymousLogger();
        anonymousLogger.log(Level.WARNING, "线程异常,终止了" + t.getName(), e);
        System.out.println(name + "捕获了异常" + t.getName() + "异常:" + e);
    }
}
public class UseUncaughtExceptionHandler implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("捕获器1"));
        new Thread(new UseUncaughtExceptionHandler(),"MyThread-1").start();
        Thread.sleep(300);
        new Thread(new UseUncaughtExceptionHandler(),"MyThread-2").start();
        Thread.sleep(300);
        new Thread(new UseUncaughtExceptionHandler(),"MyThread-3").start();
        Thread.sleep(300);
        new Thread(new UseUncaughtExceptionHandler(),"MyThread-4").start();
    }

    @Override
    public void run() {
        throw new RuntimeException();
    }
}

运行结果如下:捕获到了子线程的异常
在这里插入图片描述

UncaughtExceptionHandler 还可以给每个线程单独的设置以及给线程池设置.

  • 给每个线程单独设置
  • 给线程池设置

多线程双刃剑:可能导致安全、性能问题

思考:

  • 一共有哪几类线程安全问题(三种)?
  • 哪些场景需要额外注意线程安全问题?
  • 什么是多线程的上下文切换?
线程安全

线程安全是并发最重要的部分.什么是线程安全呢?
《Java 并发编程实战》Brian Goetz 对“线程安全"的一个描述: “当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的”.
也可以这样理解:“不管业务中遇到怎样的多个线程访问某对象或某方法的情况,而在编程这个业务逻辑的时候,都不需要额外做任何额外的处理(也就是可以像单线程编程一样),程序也可以正常运行(不会因为多线程而出错),就可以称为线程安全”

什么情况下会出现线程安全问题,怎么避免?
主要有两个问题:

  1. 数据争用:数据读写由于同时写,会造成错误数据
  2. 竞争条件 执行顺序:即使不是同时写造成的错误数据,由于顺序原因依然会造成错误,例如在写入前就读取了
  • 运行结果错误:a++ 多线程下出现消失的请求现象

如下代码,两个线程同时执行a++的操作,那么理想得到的结果是20000.

public class MultiThreadError8 implements Runnable {
    int index = 0;
    static MultiThreadError8 instance = new MultiThreadError8();

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        thread1.join();//等待线程1执行完毕
        thread2.join();//等待线程2执行完毕
        System.out.println(instance.index);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            index++;
        }
    }
}

但是得到的实际结果如下: 远远要小于20000,并且每次运行的结果还不一样,这是为什么呢?
在这里插入图片描述

i++ 在多个线程下多执行情况如下图分析: 两个线程去执行a++,我们期望的结果是3. 但是实际情况线程1
执行i=1 -> i+1 但是这时候并没有将结果赋值给i,线程2再去执行的时候拿到的结果还是i=1 -> i+1,这时候🈶️切换到线程1,此时线程1不知道线程2的步骤,执行i=2 将i=2写进去了,一旦切换到线程2,而线程2也不知道线程1 的情况,线程2执行i=2.

       线程1                                                                      线程2

在这里插入图片描述

错误分析:
下面通过代码,来打印出错误的结果

/**
 * 线程安全问题
 * 第一种:运行结果出错
 * 演示计数不准确(减少),找出具体出错的位置
 */
public class MultiThreadsError implements Runnable {

    int index = 0;

    static MultiThreadsError instance = new MultiThreadsError();

    final boolean[] marked = new boolean[10000000];

    static AtomicInteger realIndex = new AtomicInteger();
    static AtomicInteger wrongCount = new AtomicInteger();

    //CyclicBarrier 让线程根据我们的需要在某个地方等待,直到所等待的人员都就绪了,在一起出发 parties :2 表示等待两个线程
    static volatile CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
    static volatile CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        thread1.join();//等待线程1执行完毕
        thread2.join();//等待线程2执行完毕
        System.out.println(instance.index);
        System.out.println("realIndex:" + realIndex.get());
        System.out.println("wrongCount:" + wrongCount.get());

    }

    @Override
    public void run() {
        marked[0] = true;
        for (int i = 0; i < 10000; i++) {
            try {
                cyclicBarrier2.reset();
                //标记等待点 当两个线程都运行到这个点之后 一起执行
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            index++;
            try {
                //等待两个线程都执行完了 index++操作 然后放行
                cyclicBarrier.reset();
                cyclicBarrier2.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            realIndex.incrementAndGet();
            //但是存在问题 如果两个线程都执行完了index++ 操作这时候 synchronized只能有一个线程去执行
            //第一个线程执行 marked=true 而第二个线程index是一样的 执行的时候marked肯定为true 所以第二个线程肯定会打印
            //解决问题加入判断:marked[index - 1] 前一位是true
            //存在没有线程冲突的情况 很可能有没有被标记上。
            //比如 10 = true 11 = false 12 = true 正常的情况
            //错误情况 10 = true 11 = true 12
            synchronized (instance) {
                if (marked[index] && marked[index - 1]) {
                    System.out.println("发生了错误:" + index);
                    wrongCount.incrementAndGet();
                }
                marked[index] = true;
            }
        }
    }
}

运行结果如下,可以揪出错误的位置,在哪里发生了错误,更有利于我们找到运行步骤和如何出错的.
在这里插入图片描述

  • 活跃性问题:死锁 活锁 饥饿

如下代码,会造成死锁的问题synchronized中还有一个synchronized

package com.prim.background;

import java.util.Map;

/**
 * 第二个线程安全问题:死锁
 */
public class MultiThreadError implements Runnable {
    int flag = 1;

    static Object o1 = new Object();
    static Object o2 = new Object();

    public static void main(String[] args) {
        MultiThreadError r1 = new MultiThreadError();
        MultiThreadError r2 = new MultiThreadError();
        r1.flag = 1;
        r2.flag = 0;
        new Thread(r1).start();
        new Thread(r2).start();
    }

    @Override
    public void run() {
        System.out.println("flag=" + flag);
        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    System.out.println("1");
                }
            }
        }
        if (flag == 0) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    System.out.println("0");
                }
            }
        }
    }
}

  • 对象发布和初始化的时候的安全问题
  1. 方法返回一个private对象(private本意是不然外部访问)
  2. 还未完成初始化(构造函数没完全执行完毕)就把对象提供给外界:
  3. 在构造函数中未初始化完毕就this赋值
  4. 隐式逸出-注册监听事件
  5. 构造函数中运行线程
  1. 方法返回一个private对象(private本意是不然外部访问)
/**
 * 发布:public return 等
 * 逸出:1. 方法返回一个private对象(private不然外部对象访问)
 * 2. 还未完成初始化(构造函数没有完全执行完毕)就把对象提供给外界
 * 在构造函数中未初始化完毕就this赋值
 * 隐式逸出 -- 注册监听事件
 * 构造函数中运行线程
 */
public class MultiThreadError3 {
    private Map<String, Object> states;

    public MultiThreadError3() {
        states = new HashMap<>();
        states.put("1", "周一");
        states.put("2", "周二");
        states.put("3", "周三");
        states.put("4", "周四");
    }

    public Map<String, Object> getStates() {
        return states;
    }

    public static void main(String[] args) {
        MultiThreadError3 multiThreadError3 = new MultiThreadError3();
        Map<String, Object> states = multiThreadError3.getStates();
        System.out.println(states.get("1"));
        //篡改了states states本意是private 这样逸出导致了问题的发生
        states.remove("1");
        //如果多个线程调用states.get("1") 就会导致其他线程获取到的是null
        System.out.println(states.get("1"));//null
    }
}

对象初始化导致的线程安全问题:
在构造函数中未初始化完毕就this赋值

public class MultisThreadError4 {

    static Point point;

    public static void main(String[] args) throws InterruptedException {
        new PointMarker().start();
        //如下 根据线程时间休眠的不同导致结果不同 这也是线程的安全问题
//        Thread.sleep(10);//1 0
//        Thread.sleep(105);// 1 1
        if (point != null) {
            System.out.println(point);
        }
    }
}

class Point {
    private final int x, y;

    public Point(int x, int y) throws InterruptedException {
        this.x = x;
        MultisThreadError4.point = this;
        Thread.sleep(100);
        this.y = y;
    }

    @Override
    public String toString() {
        return "Point{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}

class PointMarker extends Thread {
    @Override
    public void run() {
        try {
            new Point(1, 1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}p

隐式逸出-注册监听事件: 导致的线程安全问题,演示代码如下:

/**
 * 观察者模式
 */
public class MultiThreadError5 {

    int count;

    public MultiThreadError5(MySource mySource) {
        mySource.registerListener(new EventListener() {
            @Override
            public void onEvent(Event event) {
                System.out.println("我得到的数字是:" + count);
            }
        });
        //对象还没有初始化完毕 就调用了EventListener 导致获得的count为0
        for (int i = 0; i < 10000; i++) {
            System.out.print(i);
        }
        count = 100;
    }

    public static void main(String[] args) {
        MySource mySource = new MySource();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mySource.eventCome(new Event() {
                });
            }
        }).start();
        MultiThreadError5 multiThreadError5 = new MultiThreadError5(mySource);
    }

    static class MySource {
        private EventListener eventListener;

        void registerListener(EventListener eventListener) {
            this.eventListener = eventListener;
        }

        void eventCome(Event e) {
            if (eventListener != null) {
                eventListener.onEvent(e);
            } else {
                System.out.println("还未初始化完毕");
            }
        }
    }

    interface EventListener {
        void onEvent(Event event);
    }

    interface Event {

    }
}

如下结果,构造方法还没有初始化完毕,就调用了监听,导致得到的count是0.这也是线程安全问题
在这里插入图片描述

构造函数中运行线程
如下代码,子线程的运行和构造函数的执行不是同步的,可能存在构造函数执行完毕了,子线程才开始执行,那么就会导致获取的states是null指针异常,如果Thread.sleep()等待一段时间是可以获取到states,但是这样的操作在实际开发中不允许的,这属于线程安全问题.

package com.prim.background;

import java.util.HashMap;
import java.util.Map;

public class MultiThreadError6 {
    private Map<String, Object> states;

    public MultiThreadError6() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                states = new HashMap<>();
                states.put("1", "周一");
                states.put("2", "周二");
                states.put("3", "周三");
                states.put("4", "周四");
            }
        }).start();

    }

    public Map<String, Object> getStates() {
        return states;
    }

    public static void main(String[] args) {
        MultiThreadError6 multiThreadError6 = new MultiThreadError6();
        try {
            Thread.sleep(150);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(multiThreadError6.getStates().get("1"));
    }
}

如何解决逸出?

  • 返回"副本"
    如下代码,通过返回一个新的Map而不是当前引用的map,这样如果线程1删除了某个key,对线程2没有影响
public class MultiThreadError3 {
    private Map<String, Object> states;

    public MultiThreadError3() {
        states = new HashMap<>();
        states.put("1", "周一");
        states.put("2", "周二");
        states.put("3", "周三");
        states.put("4", "周四");
    }

    public Map<String, Object> getStates() {
        return states;
    }

    /**
     * 通过副本的方法解决 发布逸出
     *
     * @return
     */
    public Map<String, Object> getStatesImproved() {
        return new HashMap<>(states);
    }

    public static void main(String[] args) {
        MultiThreadError3 multiThreadError3 = new MultiThreadError3();
        System.out.println(multiThreadError3.getStatesImproved().get("1"));
        multiThreadError3.getStatesImproved().remove("1");
        System.out.println(multiThreadError3.getStatesImproved().get("1"));
    }
}
  • 工厂模式 注册监听问题修复

将注册监听在初始化完毕后进行注册

public class MultiThreadError7 {
    int count;

    public EventListener listener;

    public MultiThreadError7(MySource mySource) {
        listener = new EventListener() {
            @Override
            public void onEvent(Event event) {
                System.out.println();
                System.out.println("我得到的数字是:" + count);
            }
        };
        //对象还没有初始化完毕 就调用了EventListener 导致获得的count为0
        for (int i = 0; i < 10000; i++) {
            System.out.print(i);
        }
        count = 100;
    }

    public static MultiThreadError7 getInstance(MySource source) {
        //先进行初始化在注册监听
        MultiThreadError7 multiThreadError7 = new MultiThreadError7(source);
        source.registerListener(multiThreadError7.listener);
        return multiThreadError7;
    }

    public static void main(String[] args) {
        MySource mySource = new MySource();
        new Thread(new Runnable() {
            @Override
            public void run() {
//                try {
//                    Thread.sleep(10);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
                mySource.eventCome(new Event() {
                });
            }
        }).start();
        MultiThreadError7.getInstance(mySource);
    }

    static class MySource {
        private EventListener eventListener;

        void registerListener(EventListener eventListener) {
            this.eventListener = eventListener;
        }

        void eventCome(Event e) {
            if (eventListener != null) {
                eventListener.onEvent(e);
            } else {
                System.out.println("还未初始化完毕");
            }
        }
    }

    interface EventListener {
        void onEvent(Event event);
    }

    interface Event {

    }
}

结果如下,这样就可以在构造函数还没有初始化完毕的时候,不去执行业务逻辑,保证执行的顺序
在这里插入图片描述

需要考虑线程安全的情况:

  • 访问共享的变量和资源,会有并发风险,比如对象的属性、静态变量、共享缓存、数据库等
  • 所有具有依赖时序性的操作,即使每一步操作都是线程安全的,还存在并发问题:read-modfiy-write(先读取再修改) check-then-act(先检查再执行)
  • 不同的数据之间存在捆绑关系的时候(IP和端口号)
  • 注意使用其他类的时候,如果对方没有声明是线程安全的,那么大概率会存在并发问题(比如HashMap它不是线程安全的)
性能问题

说到性能问题,首先我们要清楚性能问题有哪些体现、什么性能问题,以及为什么多线程会带来性能问题.

  • 性能问题有哪些体现
  • 为什么多线程会带来性能问题
    • 调度上下文切换
      • 什么是上下文? : 保存现场
      • 缓存开销 : 缓存失效
      • 何时会导致密集的上下文切换:抢锁 、 IO
    • 协作:内存同步
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值