1、进程和线程的关系

一个进程中可以有多个线程,多个线程共享进程的堆和方法区资源。但是每个线程有自己的程序计数器、虚拟机栈和本地方法栈

即,线程是进程划分成的更小的运行单位、线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定。

因为统一进程中的线程极有可能会相互影响。线程执行开销小,但不利于总院的管理和保护;而进程则相反。


2、程序计数器为何是私有的?

程序计数器主要有下面两个作用:

1、字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理

2、在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了

所以,程序计数器私有主要是为了线程切换后能恢复到正确的执行位置

2.1 如何线程安全的实现一个计数器?

可以使用加锁,比如synchronized或者lock。也可以使用Concurrent包下的原子类。


3、虚拟机栈和本地方法栈为何是私有的?

1、虚拟机栈:每个java方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数帧、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在java虚拟机栈中入栈和出栈的过程

2、本地方法栈:和虚拟机栈所发挥的作用相似。区别是:虚拟机栈为虚拟机执行java方法(即字节码)服务,而本地方法栈为虚拟机使用到的Native方法服务。

所以,为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。


4、多线程中的i++线程安全吗?为什么?

不安全。因为i++不是原子性操作

i++分为读取i 值,对i 值加一,再赋值给i++,执行期中任何一步都是有可能被其他线程抢占的。


5、多线程同步的方法

可以使用synchronized、lock、volatile 和ThreadLocal 来实现同步。


6、说说并发与并行的区别?

1、并发:两个或多个事件在同一时间间隔内发生。

并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行

2、并行:指两个或者多个事件在同一时刻发生


7、介绍一下生产者消费者模式

图片说明

生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据。

优点:支持并发、解耦。


8、线程池运行流程,参数,策略

线程池主要就是指定线程池核心线程数大小,最大线程数,存储的队列,拒绝策略,空闲线程存活时长。

当需要任务大于核心线程数时候,就开始把任务往存储任务的队列里放,当存储队列满了的话,就开始增加线程池创建的线程数量,如果当线程数量也达到了最大,就开始执行拒绝策略,比如说记录日志,直接丢弃,或者丢弃最老的任务。


9、关于AQS

所谓AQS,指的是AbstractQueuedSynchronizer,它提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架

内部实现的关键是:先进先出的队列、state 状态

定义了内部类ConditionObject

拥有两种线程模式:独占模式和共享模式。

在LOCK 包中的相关锁(常用的有ReentrantLock、ReadWriteLock)都是基于AQS 来构建,一般我们叫AQS 为同步器。

9.1、AQS组件总结

1、Semaphore(信号量)——允许多个线程同时访问:synchronized和ReentrantLock都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。

2、CountDownLatch(倒计时器):是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。不能够重用

3、CyclicBarrier(循环栅栏):一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。可以重用。


10、创建线程的方法,哪个更好,为什么?

1、从Java.lang.Thread 类派生一个新的线程类,重载它的run()方法;

2、实现Runnalbe 接口,重载Runnalbe 接口中的run()方法。

总结:实现Runnalbe 接口更好,使用实现Runnable 接口的方式创建的线程可以处理同一资源,从而实现资源的共享.


11、Java 中有几种线程池?

1、newFixedThreadPool 创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

2、newCachedThreadPool 创建一个可缓存的线程池。这种类型的线程池特点是:

  1. 工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE),这样可灵活的往线程池中添加线程。
  2. 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

3、newSingleThreadExecutor 创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

4、newScheduleThreadPool 创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。


12、线程池有什么好处?

1、降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

2、提高响应速度。当任务到达时,任务可以不需要等到线程创建就能执行。

3、提高线程的可管理性,线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。


13、如何理解Java 多线程回调方法?

所谓回调,就是客户程序C 调用服务程序S 中的某个方法A,然后S 又在某个时候反过来调用C 中的某个方法B,对于C 来说,这个B 便叫做回调方法。


14、线程的几种可用状态

1、新建( new ):新创建了一个线程对象

2、可运行( runnable ):线程对象创建后,其他线程(比如main 线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权。

3、运行( running ):可运行状态( runnable )的线程获得了cpu 时间片( timeslice ) ,执行程序代码。

4、阻塞( block ):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice ,暂时停止运行。直到线程进入可运行( runnable )状态,才有机会再次获得cpu timeslice 转到运行( running )状态。阻塞的情况分三种:

  1. 等待阻塞:运行( running )的线程执行 o.wait ()方法, JVM 会把该线程放入等待队列( waitting queue )中。
  2. 同步阻塞:运行( running )的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM 会把该线程放入锁池( lock pool )中。
  3. 其他阻塞: 运行( running )的线程执行Thread . sleep ( long ms )或t . join ()
    方法,或者发出了I / O 请求时, JVM 会把该线程置为阻塞状态。当sleep ()状态超时、join ()等待线程终止或者超时、或者I / O 处理完毕时,线程重新转入可运行( runnable )状态。

5、死亡( dead ):线程run ()、main () 方法执行结束,或者因异常退出了run ()方法,则该线程结束生命周期。死亡的线程不可再次复生。


15、sleep() 和wait() 有什么区别?

1、sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,把执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep 不会释放对象锁

2、wait 是Object 类的方法,对此对象调用wait 方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。wait()后,程序会释放同步锁。

15.1、线程的sleep()方法和yield()方法有什么区别?

1、sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;
yield()方法只会给相同优先级或者更高优先级的线程以运行的机会

2、线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后就转入就绪(ready)状态

3、sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常

4、sleep()方法比yield()方法(跟操作系统CPU 调度相关)具有更好的可移植性。


16、如何保证线程安全?

通过合理的时间调度,避开共享资源的存取冲突。另外,在并行任务设计上可以通过适当的策略,保证任务与任务之间不存在共享资源,设计一个规则来保证一个客户的计算工作和数据访问只会被一个线程或一台工作机完成,而不是把一个客户的计算工作分配给多个线程去完成。