并发编程面试题3

简介: 并发编程面试题3

可以看到,生产者生产完了,消费者立马消费,消费者消费完了,生产者又立马生产;这是一个轮流的过程,一直保持着生产者跟消费者之间的互相等待。

方法3:信号灯法

并发协作模型“生产者/消费者模式” —> 信号灯法

使用一个标志位,如果标志位为true,就等待,如果为false,就通知另一个线程。

代码模拟场景:

演员(生产者)表演节目,观众(消费者)观看节目。

用一个标志位控制线程什么时候等待,什么时候通知。

public class ThreadDemo2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
// 生产者 -> 演员
class Player extends Thread {
    TV tv;
    public Player(TV tv) {
        this.tv = tv;
    }
    // 生产
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.tv.play("电视剧第" + i + "段");
        }
    }
}
// 消费者 -> 观众
class Watcher extends Thread {
    TV tv;
    public Watcher(TV tv) {
        this.tv = tv;
    }
    // 消费
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}
// 产品 -> 节目
class TV {
    // 演员表演,观众等待
    // 观众观看,演员等待
    // 表演的节目
    String voice;
    // 标志位
    boolean flag = true;
    // 表演
    public synchronized void play(String voice) {
        if (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.voice = voice;
        System.out.println("演员表演了:" + voice);
        this.flag = !this.flag;
        // 通知观众观看
        this.notifyAll();
    }
    // 表演
    public synchronized void watch() {
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了:" + voice);
        this.flag = !this.flag;
        // 通知演员表演
        this.notifyAll();
    }
}

演员演一段,观众看一段

方法四:while轮询的方式

import java.util.ArrayList;
import java.util.List;
public class MyList {
 private List<String> list = new ArrayList<String>();
     public void add() {
     list.add("elements");
     }
     public int size() {
     return list.size();
 }
}
import mylist.MyList;
public class ThreadA extends Thread {
     private MyList list;
     public ThreadA(MyList list) {
     super();
     this.list = list;
  }
     @Override
     public void run() {
     try {
     for (int i = 0; i < 10; i++) {
         list.add();
         System.out.println("添加了" + (i + 1) + "个元素");
         Thread.sleep(1000);
       }
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
     }
}
import mylist.MyList;
public class ThreadB extends Thread {
     private MyList list;
     public ThreadB(MyList list) {
     super();
     this.list = list;
     }
     @Override
     public void run() {
     try {
         while (true) {
         if (list.size() == 5) {
         System.out.println("==5, 线程b准备退出了");
         throw new InterruptedException();
       }
      }
     } catch (InterruptedException e) {
         e.printStackTrace();
       }
     }
}
import mylist.MyList;
import extthread.ThreadA;
import extthread.ThreadB;
public class Test {
     public static void main(String[] args) {
         MyList service = new MyList();
         ThreadA a = new ThreadA(service);
         a.setName("A");
         a.start();
         ThreadB b = new ThreadB(service);
         b.setName("B");
         b.start();
     }
}

在这种方式下,线程A不断地改变条件,线程ThreadB不停地通过while语句检测这个条件(list.size()==5)是否成立 ,从而实现了线程间的通信。但是这种方式会浪费CPU资源。之所以说它浪费资源,是因为JVM调度器将CPU交给线程B执行时,它没做啥“有用”的工作,只是在不断地测试 某个条件是否成立。就类似于现实生活中,某个人一直看着手机屏幕是否有电话来了,而不是: 在干别的事情,当有电话来时,响铃通知TA电话来了。


notify()/notifyAll()/sleep()/wait()的区别

我们通过对这些方法分析,sleep()方法属于Thread类,而wait()/notify()/notifyAll()属于Object基础类,也就是说每个对象都有wait()/notify()/notifyAll()的功能。

sleep()不会释放锁,而wait()会释放锁。

sleep()必须捕获异常,而wait()/notify()/notifyAll()不需要捕获异常。

sleep()可以在任何地方使用,而wait()/notify()/notifyAll()只能在同步控制方法或者同步控制块里面使用。

介绍

notify():随机唤醒一个等待该对象同步锁的线程,进入就绪队列等待CPU的调度;这里的唤醒是由JVM确定唤醒哪个线程,而且不是按优先级决定。

notifyAll():唤醒所有的等待该对象同步锁的线程,进入就绪队列等待CPU调度;注意唤醒的是notify之前wait的线程,对于notify之后的wait线程是没有效果的。

wait():调用时需要先获得该Object的锁,调用后,会把当前的锁释放掉同时阻塞住;但可以通过调用该Object的notify()或者notifyAll()来重新获得锁。

sleep():在指定的时间内让正在执行的线程暂停执行,但不会释放锁。


同步方法和同步块,哪个是更好的选择?

同步块是更好的选择,同步块更要符合开放调用的原则,只在需要锁住的代码块锁住相应的对象,同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。


请知道一条原则:同步的范围越小越好。


借着这一条,我额外提一点,虽说同步的范围越少越好,但是在 Java 虚拟机中还是存在着一种叫做锁粗化的优化方法,这种方法就是把同步范围变大。这是有用的,比方说 StringBuffer ,它是一个线程安全的类,自然最常用的 append() 方法是一个同步方法,我们写代码的时候会反复 append 字符串,这意味着要进行反复的加锁 -> 解锁,这对性能不利,因为这意味着 Java 虚拟机在这条线程上要反复地在内核态和用户态之间进行切换,因此 Java 虚拟机会将多次 调用append 方法的代码进行一个锁粗化的操作,将多


次的 append 的操作扩展到 append 方法的头尾,变成一个大的同步块,这样就减少了加锁 --> 解锁的次数,有效地提升了代码执行的效率。


什么是线程同步和线程互斥?线程同步和互斥的区别?同步有哪几种实现方式?

线程同步

同步,又称直接制约关系,是指多个线程彼此合作,通过一定的逻辑关系来共同完成一个任务,必须严格按照规定的某种先后顺序来运行。一般来说,同步关系中往往包含互斥,同时对临界区的资源会按照某种逻辑顺序进行访问,如先生产后使用


另一种说法

互斥解决了「多进程/线程」对临界区使用的问题,但是它没有解决「多进程/线程」协同工作的问题


我们都知道在多线程里,每个线程一定是顺序执行的,它们各自独立,以不可预知的速度向前推进,但有时候我们希望多个线程能密切合作,以实现一个共同的任务。


所谓同步,就是「多进程/线程间」在一些关键点上可能需要互相等待与互通消息,这种相互制约的等待与互通信息称为「进程/线程」同步。


简单说:同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。同步其实已经实现了互斥,所以同步是一种更为复杂的互斥


线程互斥

当有若干个线程都要使用某一共享资源(共享的数据和硬件资源)时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。


简单说:互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的,互斥是一种特殊的同步。


线程同步和互斥的区别

  • 互斥是通过竞争对资源的独占使用,彼此之间不需要知道对方的存在,执行顺序是一个乱序。
  • 同步是协调多个相互关联线程合作完成任务,彼此之间知道对方存在,执行顺序往往是有序的。


实现线程同步的方法

同步代码方法:sychronized 关键字修饰的方法同步代码块:sychronized 关键字修饰的代码块

使用特殊变量域volatile实现线程同步:volatile关键字为域变量的访问提供了一种免锁机制

使用重入锁实现线程同步:reentrantlock类是可冲入、互斥、实现了lock接口的锁他与sychronized方法具有相同的基本行为和语义

使用信号量首先我们要清楚临界资源和临界区的概念,临界资源就是同一时刻只允许一个线程(或进程)访问的资源,临界区就是访问临界资源的代码段。信号量是一种特殊的变量,用来控制对临界资源的使用,在多个进程或线程都要访问临界资源的时候,就需要控制多个进行或线程对临界资源的使用。信号量机制通过p、v操作实现。p操作:原子减1,申请资源,当信号量为0时,p操作阻塞;v操作:原子加1,释放资源。


在监视器(Monitor)内部,是如何做线程同步的?

在 java 虚拟机中,监视器和锁在Java虚拟机中是一块使用的,监视器监视一块同步代码块,每一个监视器都和一个对象引用相关联,为了确保一次只有一个线程执行同步代码块(线程同步),每个对象都关联着一把锁。一旦方法或者代码块被 synchronized 修饰,那么这个部分就放入了监视器的监视区域,线程在获取锁之前不允许执行该部分的代码,确保一次只能有一个线程执行该部分的代码,另外 java 还提供了显式监视器( Lock )和隐式监视器( synchronized )两种锁方案

servlet和Struts2的action是线程安全吗?SpringMVC 的 Controller 是线程安全的吗?

servlet是线程安全吗?

Servlet 不是线程安全的,servlet 是单实例多线程的,当多个线程同时访问同一个方法,是不能保证共享变量的线程安全性的。


Struts2的action是线程安全吗?

Struts2 的 action 是多实例多线程的,是线程安全的,每个请求过来都会 new 一个新的 action 分配给这个请求,请求完成后销毁。

SpringMVC 的 Controller 是线程安全的吗?

不是的

总结

Struts2 好处是不用考虑线程安全问题;Servlet 和 SpringMVC 需要考虑线程安全问题,但是性能可以提升不用处理太多的 gc,可以使用 ThreadLocal 来处理多线程的问题。

线程的安全问题体现和导致原因和解决方案?

线程的安全性问题体现

  • 原子性:一个或者多个操作在CPU执行的过程中不被中断的特性。
  • 可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到。
  • 有序性:程序执行的顺序按照代码的先后顺序执行。


导致原因

  • 线程切换带来的原子性问题
  • 缓存导致的可见性问题
  • 编译优化带来的有序性问题


解决办法

  • JDK的Atomic开头的原子类,synchronized、Lock,可以解决原子性问题
  • synchronized、volatile、Lock,可以解决可见性问题
  • Happens-Before规则可以解决有序性问题


在 Java 程序中怎么保证多线程的运行安全?

方法一:使用安全类,比如 java.util.concurrent 下的类,使用原子类AtomicInteger

方法二:使用自动锁 synchronized。

方法三:使用手动锁 Lock。

手动锁 Java 示例代码如下:


你对线程优先级的理解是什么?

每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,这依赖于线程调度的实现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。理论上,优先级高的线程比优先级低的线程获得更多的CPU时间。实际上,线程获得的CPU时间通常由包括优先级在内的多个因素决定。线程优先级是一个 int 变量(从 1-10),1 代表最低优先级,10 代表最高优先级。


Java 的线程优先级调度会委托给操作系统去处理,所以与具体的操作系统优先级有关,如非特别需要,一般无需设置线程优先级。

线程类的构造方法和静态块是被哪个线程调用的?run方法呢?

这是一个非常刁钻和狡猾的问题。


请记住:线程类的构造方法、静态块是被 new 这个线程类所在的线程所调用的,而 run 方法里面的代码才是被线程自身所调用的。


如果说上面的说法让你感到困惑,那么我举个例子,假设 Thread2 中 new 了Thread1,main 函数中 new 了 Thread2,那么:


Thread2 的构造方法、静态块是 main 线程调用的,Thread2 的 run()方法是Thread2 自己调用的

Thread1 的构造方法、静态块是 Thread2 调用的,Thread1 的 run()方法是Thread1 自己调用的


什么是 dump 文件?你如何在 Java 中获取线程堆栈?

什么是线程dump?

线程dump是非常有用的诊断java应用问题的工具,每一个java虚拟机都有及时生成显示所有线程在某一点状态的线程dump能力。虽然每个java虚拟机线程dump打印输出格式上略微有一些不同,但是线程dump的信息包含线程基本信息、线程的运行状态、标识、调用栈;调用的堆栈包含完整的类名,所执行的方法,如果可能的话还有源代码的行数。


其中:


线程的一些基本信息:名称、优先级及id

线程状态:waiting on condition

线程的调用栈

线程锁住的资源:locked <0x3f63d600>

JVM中的许多问题都可以使用线程dump文件来进行诊断,其中比较典型的包括线程阻塞,CPU使用率过高,JVM Crash,堆内存不足和类装载等问题。


你如何在 Java 中获取线程堆栈(dump文件)?

在 windows环境中


在启动程序的控制台里敲: Ctrl +Break,线程的 dump会产生在标准输出中(缺省标准输出就是控制台,如果对输出进行了重定向,则要查看输出文件)


在 linux环境中


通过使用 jps 命令获取到线进程的 pid

通过使用 jstack pid 命令打印线程堆栈


jstack [option] pid
--参数
1. -F 强制打印堆栈
2. -m 打印java 和 native(C++) 堆栈信息  
3. -l 打印额外的信息,包括锁信息

另外提一点, Thread 类提供了一个 getStackTrace() 方法也可以用于获取线程堆栈。这是一个实例方法,因此此方法是和具体线程实例绑定的,每次获取获取到的是具体某个线程当前运行的堆栈


注意:jps 和ps的作用差不多,都是显示当前系统的java进程情况及进程id,top主要看cpu,内存使用情况及占用资源最多的进程由高到低排序,关注点在于资源占用情况


使用top查看目前正在运行的进程使用系统资源情况


当前占用cpu最高26.5%的进程为27796的java程序

使用jstack将所有线程信息导出到指定文件中

使用jstack [-l] pid > xxx.log命令将所有线程信息输入到指定文件中

当'jstack [-l] pid'没有响应,使用jstack -F [-m] [-l] pid >xxx.log命令强制导出堆栈dump

一个线程运行时发生异常会怎样?

如果异常没有被捕获该线程将会停止执行,如果该异常被捕获或抛出,则程序继续运行。


Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候,JVM 会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler 并将线程和异常作为参数传递给 handler 的uncaughtException()方法进行处理。


Java 线程数过多会造成什么异常?

(1)线程的生命周期开销非常高


(2)消耗过多的CPU 资源


如果可运行的线程数量多于可用处理器的数量,那么有线程将会被闲置。大量空闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量的线程在竞争CPU 资源时还将产生其他性能的开销。


(3)降低稳定性


JVM 在可创建线程的数量上存在一个限制,这个限制值将随着平台的不同而不同,并且承受着多个因素制约,包括JVM 的启动参数、Thread 构造函数中请求栈的大小,以及底层操作系统对线程的限制等。如果破坏了这些限制,那么可能抛出OutOfMemoryError 异常。


为什么代码会重排序?

在执行程序时,为了提高性能,处理器和编译器常常会对指令进行重排序,但是不能随意重排序,不是你想怎么排序就怎么排序,它需要满足以下两个条件:


编译器和处理器都必须遵守 as-if-serial 语义,as-if-serial 语义的意思指:不管编译器和处理器为了提高并行度怎么重排序,(单线程)程序的执行结果不能被改变。

存在数据依赖关系的不允许重排序


as-if-serial规则和happens-before规则是什么?两者的区别?

as-if-serial规则(语义)

as-if-serial 语义的意思指:不管编译器和处理器为了提高并行度怎么重排序,(单线程)程序的执行结果不能被改变。

Happens-Before规则

1、程序次序规则:在一个线程内,按照程序控制流顺序,书写在前面的操作先行发生于书写在后面的操作。


2、管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。


3、volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作。


4、线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。


5、线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测。


6、线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。


7、对象终结原则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()垃圾回收方法的开始。


as-if-serial语义保证单线程内程序的执行结果不被改变,happens- before关系保证正确同步的多线程程序的执行结果不被改变。

as-if-serial语义给编写单线程程序的程序员创造了一个幻境:单线程程序是按程序的顺序来执行的。happens-before关系给编写正确同步的多线程程序的程序员创造了一个幻境:正确同步的多线程程序是按happens- before指定的顺序来执行的。

as-if-serial语义和happens-before这么做的目的,都是为了在不改变程序执行结果的前提下,尽可能地提高程序执行的并行度。


LockSupport详解

LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,当然阻塞之后肯定得有唤醒的方法。每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,那这个线程调用park()将会立即返回,否则阻塞。如果许可不可用,则可以调用 unpark 使其可用(许可只有一个,不可累加)


LockSupport有常用的方法如下,主要有两类方法:park和unpark


public static void park(Object blocker); // 暂停当前线程
public static void parkNanos(Object blocker, long nanos); // 暂停当前线程,不过有超时时间的限制
public static void parkUntil(Object blocker, long deadline); // 暂停当前线程,直到某个时间
public static void park(); // 无期限暂停当前线程
public static void parkNanos(long nanos); // 暂停当前线程,不过有超时时间的限制
public static void parkUntil(long deadline); // 暂停当前线程,直到某个时间
public static void unpark(Thread thread); // 恢复当前线程
public static Object getBlocker(Thread t);

为什么叫park呢,park英文意思为停车。我们如果把Thread看成一辆车的话,park就是让车停下,unpark就是让车启动然后跑起来。

public class LockSupportDemo {
    public static Object u = new Object();
    static ChangeObjectThread t1 = new ChangeObjectThread("t1");
    static ChangeObjectThread t2 = new ChangeObjectThread("t2");
    public static class ChangeObjectThread extends Thread {
        public ChangeObjectThread(String name) {
            super(name);
        }
        @Override public void run() {
            synchronized (u) {
                System.out.println("in " + getName());
                LockSupport.park();
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("被中断了");
                }
                System.out.println("继续执行");
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        t1.start();
        Thread.sleep(1000L);
        t2.start();
        Thread.sleep(3000L);
        t1.interrupt();
        LockSupport.unpark(t2);
        t1.join();
        t2.join();
    }
}
in t1
被中断了
继续执行
in t2
继续执行

这儿park和unpark其实实现了wait和notify的功能,不过还是有一些差别的。


park不需要获取某个对象的锁

因为中断的时候park不会抛出InterruptedException异常,所以需要在park之后自行判断中断状态,然后做额外的处理

我们再来看看Object blocker,这是个什么东西呢?这其实就是方便在线程dump的时候看到具体的阻塞对象的信息。


"t1" #10 prio=5 os_prio=31 tid=0x00007f95030cc800 nid=0x4e03 waiting on condition [0x00007000011c9000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)
    // `下面的这个信息`
    at com.wtuoblist.beyond.concurrent.demo.chapter3.LockSupportDemo$ChangeObjectThread.run(LockSupportDemo.java:23) // 
    - locked <0x0000000795830950> (a java.lang.Object)

park和unpark的使用不会出现死锁的情况,这是因为park和unpark会对每个线程维持一

个许可(boolean值)

unpark调用时,如果当前线程还未进入park,则许可为true

park调用时,判断许可是否为true,如果是true,则继续往下执行;如果是false,则等待,直到许可为true


总结一下

park和unpark可以实现类似wait和notify的功能,但是并不和wait和notify交叉,也就是说unpark不会对wait起作用,notify也不会对park起作用。

park和unpark的使用不会出现死锁的情况

blocker的作用是在dump线程的时候看到阻塞对象的信息


hreadLocal 是什么?原理是什么?有哪些使用场景?

什么是ThreadLocal?

ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。


package com.javaBase.LineDistance;
/**
 * 〈一句话功能简述〉;
 * 〈功能详细描述〉
 *
 * @author jxx
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class TestThreadLocal {
    public static void main(String[] args) {
        ThreadLocal<Integer> threadLocal = new MyThreadLocal();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    threadLocal.set(threadLocal.get() + 1);
                    System.out.println("线程1:" + threadLocal.get());
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    threadLocal.set(threadLocal.get() + 1);
                    System.out.println("线程2:" + threadLocal.get());
                }
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    threadLocal.set(threadLocal.get() + 1);
                    System.out.println("线程3:" + threadLocal.get());
                }
            }
        });
        t1.start();
        t2.start();
        t3.start();
    }
    private static class MyThreadLocal extends ThreadLocal<Integer> {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    }
}
线程2:1
线程1:1
线程2:2
线程3:1
线程1:2
线程3:2
线程2:3
线程3:3
线程1:3

可知个线程之间对ThreadLocal的操作互不影响。

目录
相关文章
|
5月前
|
Java 开发者
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
58 0
|
安全 算法 Java
去某东面试遇到并发编程问题:如何安全地中断一个正在运行的线程
一个位5年的小伙伴去某东面试被一道并发编程的面试题给Pass了,说”如何中断一个正在运行中的线程?,这个问题很多工作2年的都知道,实在是有些遗憾。 今天,我给大家来分享一下我的回答。
95 0
|
资源调度
JUC并发编程之同步器(Semaphore、CountDownLatch、CyclicBarrier、Exchanger、CompletableFuture)附带相关面试题
1.Semaphore(资源调度) 2.CountDownLatch(子线程优先) 3.CyclicBarrier(栅栏) 4.Exchanger(公共交换区) 5.CompletableFuture(异步编程)
178 0
|
6月前
|
算法 Java 调度
《面试专题-----经典高频面试题收集四》解锁 Java 面试的关键:深度解析并发编程进阶篇高频经典面试题(第四篇)
《面试专题-----经典高频面试题收集四》解锁 Java 面试的关键:深度解析并发编程进阶篇高频经典面试题(第四篇)
73 0
|
5月前
|
Java 测试技术 开发者
Java面试题:解释CountDownLatch, CyclicBarrier和Semaphore在并发编程中的使用
Java面试题:解释CountDownLatch, CyclicBarrier和Semaphore在并发编程中的使用
74 11
|
6月前
|
存储 缓存 算法
【面试宝藏】Go并发编程面试题
探索Go语言并发编程,涉及Mutex、RWMutex、Cond、WaitGroup和原子操作。Mutex有正常和饥饿模式,允许可选自旋优化。RWMutex支持多个读取者并发,写入者独占。Cond提供goroutine间的同步,WaitGroup等待任务完成。原子操作保证多线程环境中的数据完整性,sync.Pool优化对象复用。了解这些,能提升并发性能。
92 2
|
5月前
|
设计模式 缓存 安全
Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
42 0
|
5月前
|
Java
Java面试题:Java内存模型与并发编程知识点,解释Java中“happens-before”的关系,分析Java中的内存一致性效应(Memory Consistency Effects)及其重要性
Java面试题:Java内存模型与并发编程知识点,解释Java中“happens-before”的关系,分析Java中的内存一致性效应(Memory Consistency Effects)及其重要性
32 0
|
6月前
|
安全 Java API
《面试专题-----经典高频面试题收集三》解锁 Java 面试的关键:深度解析并发编程基础篇高频经典面试题(第三篇)
《面试专题-----经典高频面试题收集三》解锁 Java 面试的关键:深度解析并发编程基础篇高频经典面试题(第三篇)
43 0
|
7月前
|
机器学习/深度学习 数据采集 自然语言处理
2024年Python最新【python开发】并发编程(下),2024年最新字节跳动的面试流程
2024年Python最新【python开发】并发编程(下),2024年最新字节跳动的面试流程
2024年Python最新【python开发】并发编程(下),2024年最新字节跳动的面试流程