线程面试题总结

线程常见面试题 阅读详情

线程面试题总结

什么是线程?

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,可以使用多线程对进行运算提速。

比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒

进程和线程的区别

  • 进程是程序运行和资源分配的基本单位
  • 一个程序至少有一个进程,一个进程至少有一个线程
  • 一个进程下也可以有多个线程来增加程序的执行速度
  • 进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高
  • 线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位
  • 同一进程中的多个线程之间可以并发执行

并行和并发有什么区别?

  并行: 多个处理器或者多核处理器同时处理多个任务。
  并发: 多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。

请看下图:
在这里插入图片描述
并发 = 两个队列和一台咖啡机。
并行 = 两个队列和两个咖啡机。

守护线程是什么?

  守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或者等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。

  一般来说,JVM(JAVA虚拟机)中一般会包括两种线程,分别是用户线程和后台线程。

  • 所谓后台线程(daemon)线程指的是:在程序运行的时候在后台提供的一种通用的服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束的时候,也就是用户线程都结束的时候,程序也就终止了。同时,会杀死进程中的所有的后台线程。
  • 反过来说,只要有任何非后台线程还在运行,程序就不会结束。比如执行main()的就是一个非后台线程。

基于这个特点,当虚拟机中的用户线程全部退出运行时,守护线程没有服务的对象后,JVM也就退出了。
所有的用户线程一定会走完,但是守护线程随着用户线程的销毁而销毁

boolean isDaemon() 
测试这个线程是否是守护线程。 
void setDaemon(boolean on) 
将此线程标记为 daemon线程或用户线程。 

创建线程有哪几种方式?

创建线程有三种方式:

  • 方式一:继承 Thread 重写 run 方法
  • 方式二:实现 Runnable 接口
  • 方式三:实现 Callable 接口

创建线程方式一和方式二的比较

  • 1、代码复杂程度:
      继承Thread方式简单。
      实现Runnable接口的方式比较复杂。
  • 2、实现原理:
      继承方式:调用 start 方法,调用 start0 方法,start0 是本地方法 private native void start0(); ,由虚拟机实现,是C语言实现的方法,所以在java中看不到源码。本地方法 start0 返回来调用java中的run方法,run方法已经在子类中重写过了,所以最终运行的是子类重写了的run方法。
      实现方式:构造器中,将Runnable的实现类对象传入构造器中,经过一路init 方法的传递,最终,用于给Thread 类型中的某个成员变量(target)赋值;调用对象的start方法,最终也是返回来调用Thread 类中的run方法,判断当前的成员变量target是否为null,如果不为null,就调用target的run方法,而这个run方法我们已经重写了,最终执行的是我们重写的run方法。

使用匿名内部类开启线程

  如果我们同时实现 Runnable接口,并且继承Thread并重写run方法,该线程会以那种方式为主?两种方式都存在则以继承Thread类为主。请看如下代码:

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("实现Runnable接口并重写run方法。。。。。。");
    }
}){
    @Override
    public void run() {
        System.out.println("Thread类的匿名内部类子类,重写run方法。。。。。");
    }
}.start();

执行结果:

Thread类的匿名内部类子类,重写run方法。。。。。

Runnable 和 Callable 有什么区别?

  • Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
  • Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

Thread类中的start()和run()方法有什么区别?

  • start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。
  • run() 方法用于执行线程的运行时代码。如果对象调用run() 方法,是普通方法色调用,并非线程的启动。
  • run() 可以重复调用。而 start() 只能调用一次,如果再次调用会抛异常。

java中用到的线程调度算法是什么

抢占式调度模型
  优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。

设置和获取线程的优先级

public final int getPriority()
public final void setPriority(int newPriority)

线程优先级:

主线程的优先级:5
线程的优先级是1-10
默认线程都是5

如果设置优先级传入的参数不合法则会抛出如下异常:

java.lang.IllegalArgumentException
异常名称: 非法参数异常
产生原因: 参数不合法
解决办法: 如果是线程说明线程的优先级不在指定范围内

什么是线程安全和线程不安全?

通俗的说:加锁的就是是线程安全的,不加锁的就是是线程不安全的

线程安全
线程安全: 就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问,直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

线程不安全
线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

java中的++操作符线程安全么?

不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,然后存储回内存,这个过程可能会出现多个线程交差

线程有哪些状态?

线程的状态:

  • NEW 尚未启动
  • RUNNABLE 正在执行中
  • BLOCKED 阻塞的 (被同步锁或者IO锁阻塞)
  • WAITING 永久等待状态
  • TIMED_WAITING 等待指定的时间重新被唤醒的状态
  • TERMINATED 执行完成

什么情况下导致线程阻塞?

  阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

方法说明
sleep()  sleep() 允许 指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。
  典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止
suspend() 和 resume()  两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应d的resume() 被调用,才能使得线程重新进入可执行状态。
  典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。
yield()  yield() 使当前线程放弃当前已经分得的CPU 时间,但不使当前线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。
  调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程
wait() 和 notify()  两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许 指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用.

sleep() 和 wait() 有什么区别

  • 所在的类不同:sleep() 来自 Thread,wait() 来自 Object。调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁
  • 释放锁:sleep() 不释放锁;wait() 释放锁。
  • 用法不同:sleep(milliseconds)需要指定一个睡眠时间,sleep() 时间到了会自动恢复;wait() 可以使用 notify() 、notifyAll()直接唤醒。
  • sleep()睡眠后不出让系统资源,wait让其他线程可以占用CPU

stop和interrupt的区别

  • stop会直接将线程生命结束
  • interrupt会给对应的线程抛出一个异常,那么对应的线程可以通过InterruptedException来捕获这个异常,并且做出相应的异常处理

什么是线程加入

线程加入:主要是在开启某一个线程后,立即执行该方法,那么该线程会在执行完毕后其它线程才能抢占 CPU 的使用权。

public final void join() 	优先让线程执行完毕

什么是线程礼让

线程礼让: 暂时让出CPU的执行权一小会,之后依然和其它线程抢占 CPU 的使用权。

public static void yield()		暂时让出CPU的执行权一小会

线程同步

线程同步:保证同一资源同步共享。
同步方式:
方式一:同步代码块

格式:synchronized(同步锁对象) {
}

方式二:同步方法

格式:
访问权限修饰符 synchronized 返回值类型 方法名(参数列表) throws 异常类 {
	return 返回值;
}

注意:
a.锁必须是同一把锁
b.锁对象可以是任意对象
c.如果是同步方法,那么锁对象是this,当前对象作锁

方式三:利用Lock锁
void lock() 获取锁。
void unlock() 试图释放此锁。

notify() 和 notifyAll() 有什么区别

  • notifyAll() 会唤醒所有的线程,notify() 只会唤醒一个线程。
  • notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而 notify() 只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。

产生死锁的条件

1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

为什么wait,nofity和nofityAll这些方法不放在Thread类当中

  一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。

怎么唤醒一个阻塞的线程

  • 如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;
  • 如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。

什么是Executors框架?

Executor框架同java.util.concurrent.Executor 接口在Java 5中被引入。

Executor框架是一个根据一组执行策略调用,调度,执行和控制的异步任务的框架。

无限制的创建线程会引起应用程序内存溢出。所以创建一个线程池是个更好的的解决方案,因为可以限制线程的数量并且可以回收再利用这些线程。

利用Executors框架可以非常方便的创建一个线程池,

创建线程池有哪几种方式?

创建线程池的方式有七种,其中最核心的是最后一种:

  • newSingleThreadExecutor() : 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目。
  • newCachedThreadPool() :它是一种用来处理大量短时间工作任务的线程池,具有如下几个鲜明的特点:
      1、它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;
      2、如果线程闲置的时间超过 60 秒,则被终止并移出缓存;
      3、长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列。
  • newFixedThreadPool() :重用指定数目(nThreads) 的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads。
  • newSingleThreadScheduledExecutor() :创建单线程池,返回ScheduledExecutorService,可以进行定时或周期性的工作调度。
  • newScheduledThreadPool(int corePoolSize) :创建一个定长线程池,和 newSingleThreadScheduledExecutor() 类似,创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。
  • newWorkStealingPool(int parallelism) :这是一个经常被人忽略的线程池,Java8 才加入这个创建方法,其内部会构建 ForkJoinPool,利用 Work-Stealing 算法,并进行地处理任务,不保证处理顺序。
  • ThreadPoolExecutor() :是最原始的线程池创建,上面的 1-3 创建方式都是对 ThreadPoolExecutor() 的封装。

为什么要使用线程池

避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目。

什么是阻塞队列?

JDK7提供了7个阻塞队列。(也属于并发容器)

  • ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
  • PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列。
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

线程池中 submit() 和 execute() 方法有什么区别?

submit() : 可以执行 Runnable 和 Callable 类型的任务。
execute() :只能执行 Runnable 类型的任务。

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

  • 方法一: 使用安全类,比如 Java.util.concurrent 下的类。
  • 方法二: 使用自动锁 synchronized。
  • 方法三: 使用手动锁。

手动锁 Java 示例代码如下:

Lock lock = new ReentrantLock();
lock.lock();
try{
	System.out.println("获取锁");
} catch (Exception e) {
	//TODO: handle exception
} finally {
	System.out.println("释放锁");
	lock.unlock();
}

多线程中 synchronized 锁升级的原理是什么?

  synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,JVM让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级为重量级锁,此过程就构成了 synchronized 锁的升级。
  锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。在 Java6 之后优化synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。

什么是死锁?

当线程 A 持有独占锁a,并尝试去获取独占锁 b 的同时,线程 B 持有独占锁 b ,并尝试获取独占锁 a 的情况下,就会发生 AB 两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。

死锁示例代码:

//代码示例一:
public class DieLock extends Thread{
	private boolean flag;
	
	public DieLock() {}
	
	public DieLock(boolean flag) {
		this.flag = flag;
	}
	
	@Override
	public void run() {
		if (flag) {
			// dl1进来
			synchronized (MyLock.lockA) {
				System.out.println("if 语句中 LockA锁"); // 就在输出完这句话之后被dl2抢到了资源
				synchronized (MyLock.lockB) {
					System.out.println("if 语句中 LockB锁");
				}
			}
		} else {
			// dl走else
			synchronized (MyLock.lockB) {
				System.out.println("else 语句中 lockB锁");
				synchronized (MyLock.lockA) {
					System.out.println("else 语句中 lockA锁");
				}
			}
		}
	}
}

/**
 * 创建锁对象
 */
class MyLock{
	public static final Object lockA = new Object();
	public static final Object lockB = new Object();
}

/**
 * 测试
 */
public class LockDemo {
	public static void main(String[] args) {
		DieLock dl1 = new DieLock(true);
		DieLock dl2 = new DieLock(false);
		
		dl1.start();
		dl2.start();
	}
}
//代码示例二:

public class DeadLock {

    private static final String A = "A";
    private static final String B = "B";

    public static void main(String[] args) {

        Thread t1 = new Thread("a"){
            @Override
            public void run() {
                synchronized (A) {
                    System.out.println(Thread.currentThread().getName() + " == A");
                    synchronized (B) {
                        System.out.println(Thread.currentThread().getName() + " == B");
                    }
                }
            }
        };
        Thread t2 = new Thread("b"){
            @Override
            public void run() {
                synchronized (B) {
                    System.out.println(Thread.currentThread().getName() + " == B");
                    synchronized (A) {
                        System.out.println(Thread.currentThread().getName() + " == A");
                    }
                }
            }
        };
        t1.start();
        t2.start();
    }
}

怎么防止死锁?

1.	尽量使用 tryLock(long  timeout,  TimeUnit  unit) 的方法 (ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。(Lock锁中就使用了这种方式)
2.	尽量使用 Java.util.concurrent 并发类代替自己手写锁。
3.	尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
4.	尽量减少同步代码块。
5.	银行家算法

ThreadLocal 是什么? 有哪些使用场景?

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

  • ThreadLocal 的使用场景:
    1、数据库连接
    2、session 管理
    3、spring 声明式事务管理

说一下 synchronized 底层实现原理?

  synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。 在 Java6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。但在 Java6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁: 偏向锁 (Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。

Java当中有哪几种锁

  • 自旋锁: 自旋锁在JDK1.6之后就默认开启了。基于之前的观察,共享数据的锁定状态只会持续很短的时间,为了这一小段时间而去挂起和恢复线程有点浪费,所以这里就做了一个处理,让后面请求锁的那个线程在稍等一会,但是不放弃处理器的执行时间,看看持有锁的线程能否快速释放。为了让线程等待,所以需要让线程执行一个忙循环也就是自旋操作。在jdk6之后,引入了自适应的自旋锁,也就是等待的时间不再固定了,而是由上一次在同一个锁上的自旋时间及锁的拥有者状态来决定
  • 偏向锁: 在JDK1.之后引入的一项锁优化,目的是消除数据在无竞争情况下的同步原语。进一步提升程序的运行性能。 偏向锁就是偏心的偏,意思是这个锁会偏向第一个获得他的线程,如果接下来的执行过程中,改锁没有被其他线程获取,则持有偏向锁的线程将永远不需要再进行同步。偏向锁可以提高带有同步但无竞争的程序性能,也就是说他并不一定总是对程序运行有利,如果程序中大多数的锁都是被多个不同的线程访问,那偏向模式就是多余的,在具体问题具体分析的前提下,可以考虑是否使用偏向锁。
  • 轻量级锁: 为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”,所以在Java SE1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁

synchronized 和 volatile 的区别是什么?

  • volatile修饰的变量,jvm每次都从主内run读取,不会从工作内存读取;
     而synchronized是锁住当前变量,同一时刻只有一个线程能访问当前变量;
     volatile只是作用于变量,synchronized可以用在变量,方法,类,代码段中
  • valatile仅能实现变量修改的可见性,无法保证原子性;synchronized可以实现变量修改的可见性和原子性。
  • volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
  • volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。

synchronized 和 Lock 有什么区别?

1.  synchronized 是 java 内置的关键字,Lock 是个接口
2.	synchronized 无法判断是否获取锁的状态,Lock 可以判断是否获取到锁.
3.	synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而Lock需要手动加锁和释放锁,如果使用不当,没有 unLock() 去释放锁就会造成死锁。
4.	synchronized 可重入(同一个类中两个同步方法,获取到锁后不用每次都去获取),不可中断,非公平;Lock可中断可公平.
5. 	synchronized 获得锁的线程阻塞,其他线程都会无限等待,Lock不会

synchronized 和 ReentrantLock 区别是什么?

synchronized 早期的实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大,但是在Java6 中对synchronized 进行了非常多的改进。

  • 1.synchronized遇到异常不catch,锁会自动释放;ReentrantLock 使用起来比较灵活,但是必须需要手动释放锁。
  • 2.synchronized是非公平锁,ReentrantLock可以实现公平锁.
  • 3.前者无法获取锁的状态,后者可以,tryLock()方法可以返回是否获得了锁.
  • 4.ReentrantLock可以精确唤醒线程,Synchronized要么随机唤醒一个,要么全部唤醒.

说一下 atomic 的原理?

  • atomic包中的类可以实现多线程环境下的变量操作,底层是调用CPU的CAS指令来进实现线程安全的。
  • atomic 主要利用 CAS(Compare And Wwap)乐观锁和 volatile 和 native 方法来保证原子操作,通过自旋保证当次修改的最终修改成功,通过降低锁粒度(多段锁)增加并发性能。
  • CAS相对于其他锁,不会进行内核态操作,有着一些性能的提升。但同时引入自旋,当锁竞争较大的时候,自旋次数会增多。cpu资源会消耗很高。
    换句话说,CAS+自旋适合使用在低并发有同步数据的应用场景。

什么是CAS

CAS,全称为Compare and Swap,即比较-替换。假设有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改为B并返回true,否则什么都不做并返回false。当然CAS一定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值A对某条线程来说,永远是一个不会变的值A,只要某次CAS操作失败,永远都不可能成功

ConcurrentHashMap的并发度是什么?

ConcurrentHashMap的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap,这也是ConcurrentHashMap对Hashtable的最大优势,任何情况下,Hashtable能同时有两条线程获取Hashtable中的数据吗?

ConcurrentHashMap的工作原理

ConcurrentHashMap在jdk 1.6和jdk 1.8实现原理是不同的:
jdk 1.6:

  • ConcurrentHashMap是线程安全的,但是与Hashtablea相比,实现线程安全的方式不同。Hashtable是通过对hash表结构进行锁定,是阻塞式的,当一个线程占有这个锁时,其他线程必须阻塞等待其释放。
  • ConcurrentHashMap是采用分离锁的方式,它并没有对整个hash表进行锁定,而是局部锁定,也就是说当一个线程占有这个局部锁时,不影响其他线程对hash表其他地方的访问。
  • 具体实现:ConcurrentHashMap内部有一个Segment

jdk 1.8:

  • 在jdk 8中,ConcurrentHashMap不再使用Segment分离锁,而是采用一种乐观锁CAS算法来实现同步问题,但其底层还是“数组+链表->红黑树”的实现。

你有哪些多线程开发良好的实践?

  • 给线程命名
  • 最小化同步范围
  • 优先使用volatile
  • 尽可能使用更高层次的并发工具而非wait和notify()来实现线程通信,如BlockingQueue,Semeaphore
  • 优先使用并发容器而非同步容器.
  • 考虑使用线程池

线程的生命周期

  • 新建状态:创建一个线程对象 Thread t = new Thread();
  • 就绪状态(可运行状态):线程对象创建后,其它线程调用了该对象的 start() 方法。 该状态的线程位于可运行线程池中,变得可运行(具备执行资格,没有执行权),等待获取CPU的使用权。
  • 运行状态:线程被分配到了CPU的时间片,执行程序代码。(具备执行资格,具备执行权)
  • 阻塞状态:阻塞状态是线程因为某种原因放弃CPU的使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

阻塞状态分三种:

等待阻塞:运行的线程执行wait() 方法,JVM 会把该线程放入等待池中。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM 会把该线程放入到锁池中。
其它阻塞:运行的线程执行 sleep() 或者 join() 方法,或者发出了 IO 请求,JVM 会把该线程设置为阻塞状态。当 sleep() 状态超时,join() 等待线程终止或者超时,或者 IO 处理完毕时,线程重新转入就绪状态。

  • 死亡状态:当 main 方法结束、线程执行完了、调用stop 方法、或者因异常退出run() 方法,该线程结束生命周期,线程对象成为垃圾对象,等待垃圾回收器回收。

如下图分析:
在这里插入图片描述

参考文献:https://juejin.im/post/5b57b81af265da0f4b7a9ae5

面试题汇总二 Java 多线程篇 前言 题目汇总来源史上最全各类面试题汇总,没有之一,不接受反驳 面试题汇总一 Java 语言基础篇 面试题汇总二 Java 多线程篇 面试题汇总三 Java 集合篇 面试题汇总四 JVM 篇 面试题汇总五 Spring 篇 面试题汇总六 数据库篇 面试题汇总七 计算机网络篇 目录 前言 多线程 开启线程的三种方式? 什么是Callable和Future? 说说进程... 阅读详情

相关推荐

CGNS快速入门到实战(二):VS配置调用CGNS静态链接库

CGNS快速入门到实战这一系列分成三篇文章展开叙述,具体有:(一)CGNS静态链接库编译;(二)VS配置调用CGNS静态链接库;(三)CGNS文件及案例展示。本篇文章重点讲述VS配置调用CGNS静态链接库。

ahang_99的博客 2618

【多线程与高并发】这可能是最全的多线程面试题了

1. 如何预防死锁? 首先需要将死锁发生的是个必要条件讲出来: 互斥条件 同一时间只能有一个线程获取资源。 不可剥夺条件 一个线程已经占有的资源,在释放之前不会被其它线程抢占 请求和保持条件 线程等待过程中不会释放已占有的资源 循环等待条件 多个线程互相等待对方释放资源 死锁预防,那么就是需要破坏这四个必要条件: 由于资源互斥是资源使用的固有特性,无法改变,我们不讨论 破坏不可剥夺条件 一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到系统的资源列表中

牧小农 1万+

PL端:HDMI 输出实验

开发板没有HDMI编码芯片,是将FPGA的3.3差分IO直接连接到HDMI连接器,FPGA 完成 24 位 RGB 编码输出TMDS 差分信号。1、学会使用第三点IP核,首先是添加,后面的使用和自带IP核操作一致。2、主要还是在梳理vivado使用流程。

qq_43441284的博客 4287

不会多线程还想进BAT?精选19道多线程面试题,有答案边看边学

欢迎关注专栏:Java架构技术进阶。里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦。 一. Java程序如何停止一个线程? 建议使用”异常法”来终止线程的继续运行。在想要被中断执行的线程中, 调用 interrupted()方法,该方法用来检验当前线程是否已经被中断,即该线程 是否被打上了中断的标记,并不会使得线程立即停止运行,如果返回 tru...

玩赚AI大模型的博客 659

【学一点英飞凌】AutoSar-MCAL-Gtm-TOM模块

本文基于英飞凌TC3X7芯片手册,GTM的TOM模块是产生并输出PWM的地方,想要彻底了解PWM,就必须知道它是怎么产生和如何被输出的。TOM通道还没有介绍的,详见下一篇;

qq_44688936的博客 6030

Sciter教程_00_创建HelloWorld工程

本教程记录Sciter工程的创建和编译运行的过程。

qq_36707072的博客 2277

Java多线程 常见面试题

Java多线程 常见面试题

yuanchun的知识整理 2820

C语言实现AES加解密

C语言实现AES加解密 #include<stdio.h> #include<string.h> #include<time.h> #include<windows.h> #define Nb 4 //数据块行数 #define Nk 4 //密码块行数 #define Nr 10 //轮加密次数 unsigned char RoundKey[4*Nb*(Nr+1)]; //轮密钥 unsigned char Key[17];

qq_44310495的博客 8890

Java面试题总结(乱序版,2020-09-03)

一、如何实现数组和 List 之间的转换? 二、什么是锁消除和锁粗化? 三、为什么说 Synchronized 是一个悲观锁?乐观锁的实现原理又是什么?什么是 CAS,它有什么特性? 四、乐观锁一定就是好的吗? 五、session 和 cookie 有什么区别? 六、tcp 为什么要三次握手,两次不行吗?为什么? 七、使用工厂模式最主要的好处是什么?在哪里使用? 八、什么是 Spring inner beans? 九、Spring 框架中的单例 Beans 是线程安全的么? 十、如何集成

学Java,找哪吒 2万+

使用Python爬虫抓取政府网站政务信息:公告、通知的自动化抓取与分析

通过结合Requests、BeautifulSoup、Selenium等技术,你能够高效地抓取政府网站的数据,并通过MySQL存储与Pandas分析这些数据。通过自动化手段抓取这些信息,不仅可以提高数据收集的效率,还能够实时获取最新的政务动态,为政策研究、舆情分析等提供宝贵数据。我们将通过Python的Requests、BeautifulSoup、Selenium等工具,结合最新的技术实现一个完整的爬虫系统,帮助你轻松抓取并分析政务信息。通过爬虫,我们可以自动化地从指定网站上抓取公告、新闻、通知等信息。

2201_76125261的博客 1907

STM32F103C8T6驱动DHT11温湿度传感器程序

本程序基于 STM32F103C8T6 微控制器,通过 GPIO 模拟单总线通信时序实现对 DHT11 温湿度传感器的数据采集,主机采用拉低数据线的方式触发传感器启动,并通过检测高电平持续时间区分逻辑“0”和“1”,依次读取湿度和温度的整数数据及校验位,经过校验后将有效结果存入缓存数组,实现对环境温湿度的周期性测量与处理。

加斯顿工程师的博客 200

Java线程面试题

Java线程面试题 Top 50 下面是Java线程相关的热门面试题,你可以用它来好好准备面试。 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。Java在语言层面对多线程提供了卓越的...

ANGEL CITY 1917

TRAE智能体开发:TRAE概述

TRAE(/treɪ/)深度融合 AI 能力,是一名能够理解需求、调用工具并独立完成各类开发任务的“AI 开发工程师”,帮助你高效推进每一个项目。

王国平的博客 1267

Java最新线程面试题

它是为创建代价高昂的对象获取线程安全的好方法,比如你可以用ThreadLocal让SimpleDateFormat变成线程安全的,因为那个类创建代价高昂且每次调用都需要创建不同的实例所以不值得在局部范围使用它,如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。

2401_89221445的博客 1022

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

java线程池的面试题_献给准备面试的你,Java线程and线程池面试题小结

最近这几天一直在整理Java相关的面试题,“金九银十”是求职的最佳时间,但是现在的“银十”也已经过去了一半的时间,相信现在还在为面试四处奔波的小伙伴已经很疲惫了吧,下面就来减轻你负担,Java线程和线程池相关的面试题整理给大家,减轻你准备面试的负担。丑话说在前面,我“丑”我先说,嘿嘿。因为篇幅有限,所以这次的文章不会包含面试题的所有的内容,在这里求大家点一波关注啦!以后会持续更新哒!1、为什么用线...

weixin_42140716的博客 1212

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

【面试题系列】Java 多线程面试题深度解析

本文涉及Java 多线程面试题,从基础到高级,希望对你有所帮助!

颜淡慕潇 5771

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Java线程经典面试题

2016-07-1815:38:26 53道Java线程面试题 下面是Java线程相关的热门面试题,你可以用它来好好准备面试。 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需1...

abc165015的博客 163

Java多线程面试题大作战:线程、锁、线程池全解析

1,任务在提交的时候,首先判断核心线程数是否已满,如果没有满则直接添加到工作线程执行2,如果核心线程数满了,则判断阻塞队列是否已满,如果没有满,当前任务存入阻塞队列3,如果阻塞队列也满了,则判断线程数是否小于最大线程数,如果满足条件,则使用临时线程执行任务如果核心或临时线程执行完成任务后会检查阻塞队列中是否有需要执行的线程,如果有,则使用非核心线程执行任务4,如果所有线程都在忙着(核心线程+临时线程),则走拒绝策略。公平锁的效率往往没有非公平锁的效率高,在许多线程访问的情况下,公平锁表现出较低的吞吐量。

m0_68778396的博客 1683

java 线程面试题_50道Java线程面试题分析及答案

下面是Java线程相关的热门面试题,你可以用它来好好准备面试。1) 什么是线程?线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成一个任务只需10毫秒。Java在语言层面对多线程提供了卓越的支持,它也是一个很好的卖点。2) 线程和...

weixin_30415591的博客 2114

多线程高频基础面试题

一. 线程与进程的区别 二. 并发与并行的区别 三. 创建线程的方式有哪些 四. Runnable和Callable有什么区别 五. 线程包括哪些状态,状态之间是如何变化的 六. 在Java中wait和sleep方法的不同 七. 新建三个线程,如何保证它们按顺序执行 八. notify()和notifyAll()有什么区别 九. 线程的run()和start()有什么区别 十. 如何停止一个正在运行的线程

几许的博客 1261

Java线程面试题合集(含答案)

来源:Java线程面试题下面是我自己收集整理的Java线程相关的面试题,可以用它来好好准备面试。 参考文档: 《Java核心技术 卷一》 Java线程面试题 Top 50:http://www.importnew.com/12773.html JAVA多线程和并发基础面试问答: http://ifeve.com/java-multi-threading-concurrency-inte...

Tanyboye的博客 6万+

线程池面试题

难3出现4ThreadPoolExecutor的7个核心参数corePoolSize核心线程数量3maximumPoolSize最大线程数5:(核心线程数+临时线程最大数2)keepAliveTime生存时间(临时线程的存活时间)unit时间单位(临时线程数的生存单位 秒或者毫秒)workQueue阻塞队列:没有空闲的核心线程进入阻塞队列,队慢进入临时线程执行任务threadFactory线程工厂指定线程创建,设置名字是否是守护线程handle拒绝策略。

weixin_68344824的博客 1830

关于Java线程池相关面试题

suns面试题

weixin_43996338的博客 651

50道Java线程面试题

不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题。Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员的欢迎。大多数待遇丰厚的Java开发职位都要求开发者精通多线程技术并且有丰富的Java程序开发、调试、优化经验,所以线程相关的问题在面试中经常会被提到。在典型的Java面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程, 如何创建线程,用什么方式...

攻城狮不是猫 264

关于线程同步的面试题一

关于线程同步的面试题一

heliuerya的博客 751
上一篇: MyCat的分片规则(一)
下一篇: Spring事务管理全面分析
知性人
博客等级 码龄9年 102粉丝 93原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值