1、在 java 中守护线程和本地线程区别?
守护线程:就是程序运行在后台提供一种通用服务线程,比如垃圾回收就是一个守护线程并且它在程序中不一定要有的部分。所以所有非守护线程结束时程序就结束,同时会杀死进程中的所有守护线程。与此同时只要任何非守护线程还在运行,程序就不会结束。本地线程:就是自己创建的线程(new Thread).区别:唯一的不同之处就是在虚拟机离开时,如果本地线程全部退出运行,只剩下守护线程存在了,虚拟机也就退出了。因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
2、线程与进程的区别?
其实很简单,实现一个进程里面有一个线程或者多个线程,比如:你们电脑上面的软件随便打开一个就是一个进程,软件里面的功能运行就是一个线程(这是我理解的)。1. 线程执行开销小,但是不利于资源的管理和保护。线程适合在SMP机器(双CPU系统)上运行。2. 进程执行开销大,但是能够很好的进行资源管理和保护。进程可以跨机器前移。
3、什么是多线程中的上下文切换?
多线程会共同使用一组计算机上的CPU,而线程数大于给程序分配的CPU数量时,为了让各个线程都有执行的机会,就需要轮流使用CPU。
不同的线程切换使用CPU发生的数据切换等就是上下文切换。
4、死锁与活锁的区别,死锁与饥饿的区别?
死锁:是两个或者两个以上的线程(进程)同时进行中,因为同时竞争同一资源造成一种相互等待状态,永远都处于等待状态的进程称为死锁。活锁:就是这个线程很像一个绅士一样它们喜欢互相谦让,所以导致资源没有人占用,所以活锁不会出现阻塞,但是活锁非常浪费CPU的时间。饥饿:它和活锁一样永远处于等待状态,不同的是它是A占用资源Y,B线程就请求封锁Y,所以B处于等待状态。C也要请求资源Y,A释放Y的封锁后,系统允许了C的请求,B处于等待状态后面D,E,F同上…请求,B永远处于等待状态。死锁与活锁的区别:不同之处在于处于活锁的线程或进程的状态是一直在不断改变的,比如A和B都想去上厕所,厕所只有一个,A和B互相谦让,导致没有人上厕所。简单的来说活锁和死锁的主要区别是前者 进程的状态可以改变但是却不能继续执行。死锁与饥饿的区别:进程会处于饥饿状态是因为持续地有其它优先级更高的进程请求相同的资源。不像死锁或者活锁,饥饿能够被解开。例如,当其它高优先级的进程都终止时并且没有更高优先级的进程到达。
5、Java 中用到的线程调度算法是什么?
操作系统的核心,它实际就是一个常驻内存的程序,不断地对线程队列进行扫描,利用特定的算法(时间片轮转法、优先级调度法、多级反馈队列调度法等)找出比当前占有CPU的线程更有CPU使用权的线程,并从之前的线程中收回处理器,再使待运行的线程占用处理器。
6、什么是线程组,为什么在 Java 中不推荐使用?
首先要明确的是,如同数据库的连接和建立一样,线程的启动和停止对JVM和操作系统而言都是一件开销很大的事情。
线程池的目的就是为了避免线程被频繁的创建,启动和停止。最后,而之所以要提出“线程组”的概念,很难从字面上找到原因。这多少为我们讨论的主题带来了一些混乱。一般地说,我们认为是由于“安全”或者“保密”方面的理由才使用线程组的。根据Arnold和Gosling的说法:“线程组中的线程可以修改组内的其他线程,包括那些位于分层结构最深处的。一个线程不能修改位于自己所在组或者下属组之外的任何线程”(注释①)。然而,我们很难判断“修改”在这儿的具体含义是什么。下面这个例子展示了位于一个“叶子组”内的线程能修改它所在线程组树的所有线程的优先级,同时还能为这个“树”内的所有线程都调用一个方法。
7、为什么使用 Executor 框架?
1.每次执行任务创建线程new Thread比较消耗性能,创建线程消耗时消耗源。2,.调用 new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的频繁交替也会消耗很多系统资源。3. 接使用new Thread() 启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不便实现。
8、在 Java 中 Executor 和 Executors 的区别?
1.Executor:它是一个执行接口,来执行任务的,准确的说,Executor提供了execute()接口来执行已经提交的Runnable任务对象。Executor存在目的是提供一种“任务提交”与“任务如何执行”分离开来的机制。它包含一个函数接口。
2.Executors是个静态工厂类。它通过静态工厂方法返回ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 等类的对象.
9、如何在 Windows 和 Linux 上查找哪个线程使用的 CPU 时间最长?1. windows上面用任务管理器看,linux下可以用top这个工具看。(如果你要查具体的进程,可以用ps命令,比如:java:ps-ef|grep java)
10、什么是原子操作?在 Java Concurrency API 中有哪些原子类(atomic classes)?1.原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。int++并不是一个原子操作,所以当一个线程读取它的值并加1时,另外一个线程有可能会读到之前的值,这就会引发错误。2.为了解决这个问题,必须保证增加操作是原子的,在JDK1.5之前我们可以使用同步技术来做到这一点。到JDK1.5,java.util.concurrent.atomic包提供了int和long类型的原子包装类,它们可以自动的保证对于他们的操作是原子的并且不需要使用同步。3.java.util.concurrent这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。
11、Java Concurrency API 中的 Lock 接口(Lock interfa是什么?对比同步它有什么优势?1. Lock接口比同步方法和同步块提供了更具扩展性的锁操作。他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。2. 优势:可以使锁更公平
可以使线程在等待锁的时候响应中断
可以让线程尝试获取锁,并在无法获取锁的时候立即返回或者等待一段时间
可以在不同的范围,以不同的顺序获取和释放锁
12、什么是 Executors 框架?
Executors是一个工厂类,可以方便的创建各种类型Executor或线程池:· newSingleThreadExecutor():创建一个Executor,顺序执行一个任务队列,每次只能执行一个任务。· newFixedThreadPool(nThreads):创建一个有固定数目线程的线程池,每次最多执行nThreads个任务,如果任务多于nThreads,多于的线程置于等待队列中· newCachedThreadPool():创建一个线程池,线程数目会随任务数目增加而增加,同时也会回收已经空闲的线程。· newScheduledThreadPool(corePoolSize):创建一个线程池,可以让任务延迟或周期性执行。
13、什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻塞队列来实现生产者-消费者模型?
1.阻塞队列是一个在队列基础上又支持了两个附加操作的队列。(1. )支持阻塞的插入方法:队列满时,队列会阻塞插入元素的线程,直到队列不满。(2. )支持阻塞的移除方法:队列空时,获取元素的线程会等待队列变为非空。2应用场景:阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。简而言之,阻塞队列是生产者用来存放元素、消费者获取元素的容器。3原理:其实阻塞队列实现阻塞同步的方式很简单,使用的就是是lock锁的多条件(condition)阻塞控制。使用BlockingQueue封装了根据条件阻塞线程的过程,而我们就不用关心繁琐的await/signal操作了。3. BlockingQueue 接口是java collections框架的一部分,它主要用于实现生产者-消费者问题。
14、什么是 Callable 和 Future?(获取执行结果)
Java5在concurrency包中引入了java.util.concurrent.Callable接口,它和Runnable接口很相似,但它可以返回一个对象或者抛出一个异常。
Callable接口使用泛型去定义它的返回类型Executors类提供了一些有用的方法去在线程池中执行Callable内的任务。由于Callable任务是并行的,必须等待它返回的结果。java.util.concurrent.Future对象解决了这个问题。在线程池提交Callable任务后返回了一个Future对象,使用它可以知道Callable任务的状态和得到Callable返回的执行结果。Future提供了get()方法,等待Callable结束并获取它的执行结果。
15、什么是 FutureTask?使用 ExecutorService 启动任务。
1.FutureTask类是Future的实现,它同时也实现了Runnable,因此也可以被Executor执行。2.https://www.iteye.com/blog/shift-alt-ctrl-1841088
16、什么是并发容器的实现?
何为同步容器:可以简单地理解为通过synchronized来实现同步的容器,如果有多个线程调用同步容器的方法,它们将会串行执行。比如Vector,Hashtable,以及Collections.synchronizedSet,synchronizedList等方法返回的容器。 可以通过查看Vector,Hashtable等这些同步容器的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并在需要同步的方法上加上关键字synchronized。 并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性,例如在ConcurrentHashMap中采用了一种粒度更细的加锁机制,可以称为分段锁,在这种锁机制下,允许任意数量的读线程并发地访问map,并且执行读操作的线程和写操作的线程也可以并发的访问map,同时允许一定数量的写操作线程并发地修改map,所以它可以在并发环境下实现更高的吞吐量。17、多线程同步和互斥有几种实现方法,都是什么?
线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。
18、什么是竞争条件?你怎样发现和解决竞争?
在Java多线程中,当两个或以上的线程对同一个数据进行操作的时候,可能会产生“竞争条件”的现象。这种现象产生的根本原因是因为多个线程在对同一个数据进行操作,此时对该数据的操作是非“原子化”的,可能前一个线程对数据的操作还没有结束,后一个线程又开始对同样的数据开始进行操作,这就可能会造成数据结果的变化未知。19、你将如何使用 thread dump?你将如何分析 Thread dump?
https://blog.csdn.net/l1394049664/article/details/81290910https://my.oschina.net/farces/blog/676731
20、为什么我们调用 start()方法时会执行 run()方法,为什么我们不能直接调用 1.如果我们直接调用子线程的run()方法,其方法还是运行在主线程中,代码在程序中是顺序执行的,所以不会有解决耗时操作的问题。所以不能直接调用线程的run()方法,只有子线程开始了,才会有异步的效果。当thread.start()方法执行了以后,子线程才会执行run()方法,这样的效果和在主线程中直接调用run()方法的效果是截然不同的。2.run()方法:如果我们直接调用子线程的run()方法,其方法还是运行在主线程中,代码在程序中是顺序执行的,所以不会有解决耗时操作的问题。所以不能直接调用线程的run()方法,只有子线程开始了,才会有异步的效果。当thread.start()方法执行了以后,子线程才会执行run()方法,这样的效果和在主线程中直接调用run()方法的效果是截然不同的。
21、Java 中你怎样唤醒一个阻塞的线程?
有4种方式:1. suspend与resume(挂起状态)。2. wait与notify(wait与notify必须配合synchronized使用,因为调用之前必须持有锁,wait会立即释放锁,notify则是同步块执行完了才释放)3. await与singal(与wait和notify相同,因为使用Lock锁后无法使用wait方法)4. park与unpark(可以唤醒指定线程)22、在 Java 中 CycliBarriar 和 CountdownLatch 有什么区别?1. 辅助类2. CycliBarriar可以重复使用,CountdownLatch不可以重复使用3. 对于CountDownLatch来说,重点是那个“一个线程”,是它在等待,而另外那N的线程在把“某个事情”做完之后可以继续等待,可以终止。而对于CyclicBarrier来说,重点是那N个线程,他们之间任何一个没有完成,所有的线程都必须等待。
23、什么是不可变对象,它对写并发应用有什么帮助?
1.不可变对象是指一个对象的状态在对象被创建之后就不再变化。不可变对象对于缓存是非常好的选择,因为你不需要担心它的值会被更改
24、什么是多线程中的上下文切换?
1.上下文切换是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征
25、Java 中用到的线程调度算法是什么?
抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。操作系统中可能会出现某条线程常常获取到VPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。
27、为什么使用 Executor 框架比使用应用创建和管理线程好?
Executor框架是基于线程池的,可以实现线程的总量控制和重用。直接使用线程有可能会耗尽系统的线程资源,因为Java的线程一般都是直接对应操作系统的基础设施的。
28、java 中有几种方法可以实现一个线程?
1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用Callable和FutureTask创建线程
4)使用线程池,例如用Executor框架
5)Spring实现多线程(底层是线程池)
6)定时器Timer (底层封装了一个TimerThread对象)
29、如何停止一个正在运行的线程?
1.使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。2.使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。3.使用interrupt方法中断线程。
30、notify()和 notifyAll()有什么区别?
如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
31、什么是 Daemon 线程?它有什么意义?
java线程还分用户线程和守护线程daemon线程即为守护线程
用户线程:就是我们平时创建的普通线程。
守护线程:主要是用来服务用户线程。
在jvm里,如果所有用户线程结束,那么守护线程也会被终止。
32、java 如何实现多线程之间的通讯和协作?
1.1. wait/notifywait 和 notify 是 Object 类的两个方法,理解起来还是有些复杂的。它和多线程同步有关系,个人觉得放在 Object 类不太合理,可能是历史遗留问题吧。每个对象都有一把锁(monitor),在进入同步方法或代码块之前,当前线程需要先获取对象锁,然后才能执行同步块的代码,完成后释放对象锁。锁可以理解为唯一的凭证,有了它就能入场,而且独占所有的资源,立场就得交出去。2. Lock/ConditionCondition 可以看作 Object 的 wait/notify 的替代方案,同样用来实现线程间的协作。与使用 wait/notify 相比,Condition的 await/signal 更加灵活、安全和高效。Condition 是个接口,基本的方法就是 await() 和 signal()。Condition 依赖于 Lock 接口,生成一个 Condition 的代码是 lock.newCondition() 。 需要注意 Condition 的 await()/signal() 使用都必须在lock.lock() 和 lock.unlock() 之间才可以,Conditon 和 Object 的 wait/notify 有着天然的对应关系:
33、什么是可重入锁(ReentrantLock)?
就是可以重新获取锁。可重入的意思是线程可以重复获得它已经持有的锁。Java的synchronized块是可重入的。
34、当一个线程进入某个对象的一个 synchronized 的实例方法后,其它线程是否可进入此对象的其它方法?
不能,一个对象的一个synchronized方法只能由一个线程访问
35、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
1、悲观锁,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住。【数据锁定:数据将暂时不会得到修改】2、乐观锁,认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息。让用户决定如何去做。实现方式:1. 悲观锁:大多数情况下依靠数据库的锁机制实现。乐观锁:大多数基于数据版本(Version)记录机制实现。
36、SynchronizedMap 和 ConcurrentHashMap 有什么区别?
Collections.synchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步,而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要要有一个线程访问map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程,仍然可以对map执行某些操作。这样,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,所以,即使在遍历map时,其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。 但是,细心的朋友可能发现了,上面的例子,即使map=map3时,最后打印的结果可以并没有100行。由于,不论Collections.synchronizedMap()还是ConcurrentHashMap对map同步的原子操作都是作用的map的方法上,map在读取与清空之间,线程间是不同步的。上面代码的不足在于,我们对这些同步的map过于信任,而忽略了混合操作带来的影响。正确的方法是,把map的读取和清空看成一个原子操作,给整个代码块加锁。 还有一个区别是:ConcurrentHashMap从类的命名就能看出,它必然是个HashMap。而Collections.synchronizedMap()可以接收任意Map实例,实现Map的同步37、CopyOnWriteArrayList 可以用于什么应用场景?
可以直接在CopyOnWriteArrayList上一边遍历,一遍进行add、remove操作:我们都知道,对于ArrayList等集合,遍历的时候如果调用add、remove修改list,会报ConcurrentModificationException异常。但是,CopyOnWriteArrayList由于采用副本,所以可以直接add、remove。
38、什么叫线程安全?servlet 是线程安全吗?· 1.当多个线程访问某个方法时,不管你通过怎样的调用方式或者说这些线程如何交替的执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的
2.Servlet 默认是单例模式,在web 容器中只创建一个实例,所以多个线程同时访问servlet的时候,Servlet是线程不安全的。
39、volatile 有什么用?能否用一句话说明下 volatile 的应用场景?
volatile保证内存可见性和禁止指令重排。volatile用于多线程环境下的单次操作(单次读或者单次写)。volatile关键字不能提供原子性。
volatile关键字为实例域的同步访问提供了一种免锁机制。如果声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新。
40、为什么代码会重排序?
41、在 java 中 wait 和 sleep 方法的不同?
这两个方法来自不同的类分别是Thread和Object · 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法(锁代码块和方法锁)。 · wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围) · sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常 · sleep方法属于Thread类中方法,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态,因为线程调度机制恢复线程的运行也需要时间,一个线程对象调用了sleep方法之后,并不会释放他所持有的所有对象锁,所以也就不会影响其他进程对象的运行。但在sleep的过程中过程中有可能被其他对象调用它的interrupt(),产生InterruptedException异常,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。 · 注意sleep()方法是一个静态方法,也就是说他只对当前对象有效,通过t.sleep()让t对象进入sleep,这样的做法是错误的,它只会是使当前线程被sleep 而不是t线程 · wait属于Object的成员方法,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程;如果线程拥有某个或某些对象的同步锁,那么在调用了wait()后,这个线程就会释放它持有的所有同步资源,而不限于这个被调用了wait()方法的对象。wait()方法也同样会在wait的过程中有可能被其他对象调用interrupt()方法而产生
42、用 Java 实现阻塞队列使用wait和notify实现Queue * BlockingQueue: 顾名思义,首先它是一个队列,并且支持阻塞机制,阻塞的放入和阻塞的得到数据, * 我们要实现LinkedBlockingQueue下面下面两个简单的方法put和take * put(anObject):把把对象加到BlockingQueue里面,如果BlockQueue没有空间,则调用此方法的线程被阻断 * 直到BlockingQueue里面没有空间在继续。 *take():取走blockingQueue里面该在首位的对象,若blockingQueue为空,阻塞进入等待状态,直到BlockingQueue有新的数据被加入 * * wait 和notify 结合synchronized使用
43、一个线程运行时发生异常会怎样?
https://blog.csdn.net/li_mengjun/article/details/78163018
44、如何在两个线程间共享数据?
1,如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,卖票系统就可以这么做。 2,如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,例如,设计4个线程。其中两个线程每次对j增加1,另外两个线程对j每次减1,银行存取款
45、Java 中 notify 和 notifyAll 有什么区别?
Java提供了两个方法notify和notifyAll来唤醒在某些条件下等待的线程,你可以使用它们中的任何一个,但是Java中的notify和notifyAll之间存在细微差别,这使得它成为Java中流行的多线程面试问题之一。当你调用notify时,只有一个等待线程会被唤醒而且它不能保证哪个线程会被唤醒,这取决于线程调度器。虽然如果你调用notifyAll方法,那么等待该锁的所有线程都会被唤醒,但是在执行剩余的代码之前,所有被唤醒的线程都将争夺锁定,这就是为什么在循环上调用wait,因为如果多个线程被唤醒,那么线程是将获得锁定将首先执行,它可能会重置等待条件,这将迫使后续线程等待。因此,notify和notifyAll之间的关键区别在于notify()只会唤醒一个线程,而notifyAll方法将唤醒所有线程。46、为什么 wait, notify 和 notifyAll 这些方法不在 thread 类里面?1.wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中,因为锁属于对象。
47、什么是 ThreadLocal 变量?
1.ThreadLocal是一种变量类型,我们称之为“线程局部变量”2.每个线程访问这种变量的时候都会创建该变量的副本,这个变量副本为线程私有3.ThreadLocal类型的变量一般用private static加以修饰
48、Java 中 interrupted 和 isInterrupted 方法的区别?
前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其它线程调用中断来改变
49、为什么 wait 和 notify 方法要在同步块中调用?
等待方遵循的原则: - 获取对象的锁 - 不满足条件 就调用wait()方法 - 条件满足继续执行 通知方原则: - 获取对象的锁 - 改变条件, 然后notify问题: - 为什么wait、notify必须被同步块包裹着? - notify之后 一定会立刻唤醒么?synchronized代码块通过javap生成的字节码中包含 monitorenter 和 monitorexit 指令。
50、为什么你应该在循环中检查等待条件?
处于等待状态的线程可能会收到错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出.
51、Java 中的同步集合与并发集合有什么区别?
同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性。
52、什么是线程池? 为什么要使用它?
为了减少创建和销毁线程的次数,让每个线程可以多次使用,可根据系统情况调整执行的线程数量,防止消耗过多内存,所以我们可以使用线程池.java中线程池的顶级接口是Executor(e可rai kei ter),ExecutorService是Executor的子类,也是真正的线程池接口,它提供了提交任务和关闭线程池等方法。调用submit方法提交任务还可以返回一个Future(fei 曲儿)对象,利用该对象可以了解任务执行情况,获得任务的执行结果或取消任务由于线程池的配置比较复杂,JavaSE中定义了Executors类就是用来方便创建各种常用线程池的工具类。通过调用该工具类中的方法我们可以创建单线程池(newSingleThreadExecutor),固定数量的线程池(newFixedThreadPool),可缓存线程池(newCachedThreadPool),大小无限制的线程池(newScheduledThreadPool),比较常用的是固定数量的线程池和可缓存的线程池,固定数量的线程池是每提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行.可缓存线程池是当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行.。
53、怎么检测一个线程是否拥有锁?
1)我想过,当wait()和notify()方法从非同步上下文中调用时会抛出IllegalMonitorStateException,所以我说我会调用newspaper.wait(),如果这个调用抛出异常,这意味着Java中的线程并未持有锁,否则线程持有锁。 2)后来我发现线程有一个名为holdsLock(Object obj)的静态方法,它根据线程是否对传递的对象持有锁来返回true或false。
您还可以学习Cay S. Horstmann的Core Java Volume 1 - Fundamentals或Java Threads 2nd Editon,以了解更多关于Java中的Thread类的内容。第一本书是以最容易的方式学习Java秘密的最佳书籍之一。
这就是如何找到一个线程是否拥有Java中某个特定对象的锁。如果您需要在Java应用程序中知道这一点,则应该使用java.lang.Thread类的holdsLock()方法。
54、你如何在 Java 中获取线程堆栈?
Java是一门基于JVM虚拟机技术的高级编程语言,在不同的操作系统平台上有不同类型的Java虚拟机,不同平台的虚拟机保证了Java一次编写处处运行,因而Java语言在服务器编程方面有着很大优势。
56、Thread 类中的 yield 方法有什么作用?
1.我们知道 start() 方法是启动线程,让线程变成就绪状态等待 CPU 调度后执行.2.yield 即 “谦让”,也是 Thread 类的方法。它让掉当前线程 CPU 的时间片,使正在运行中的线程重新变成就绪状态,并重新竞争 CPU 的调度权。它可能会获取到,也有可能被其他线程获取到
57、Java 中 ConcurrentHashMap 的并发度是什么?
并发度可以理解为程序运行时能够同时更新ConccurentHashMap且不产生锁竞争的最大线程数,实际上就是ConcurrentHashMap中的分段锁个数,即Segment[]的数组长度。ConcurrentHashMap默认的并发度为16,但用户也可以在构造函数中设置并发度。当用户设置并发度时,ConcurrentHashMap会使用大于等于该值的最小2幂指数作为实际并发度(假如用户设置并发度为17,实际并发度则为32)。运行时通过将key的高n位(n = 32 – segmentShift)和并发度减1(segmentMask)做位与运算定位到所在的Segment。segmentShift与segmentMask都是在构造过程中根据concurrency level被相应的计算出来。 如果并发度设置的过小,会带来严重的锁竞争问题;如果并发度设置的过大,原本位于同一个Segment内的访问会扩散到不同的Segment中,CPU cache命中率会下降,从而引起程序性能下降。(文档的说法是根据你并发的线程数量决定,太多会导性能降低)
58、Java 中 Semaphore 是什么?Semaphore(信号量)是用来控制访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。Semaphore可以用于做流量控制,特别是共用资源的应用场景。
1、Semphore可以控制并发的线程数 2、Semphore中的许可证数是动态的,每当调用一次release方法,许可证数加1 3、当Semphore构造时传入1时,他可以当做互斥锁使用,等于1表示可获取,等于0表示要等待。
59、Java 线程池中 submit() 和 execute()方法有什么区别?
区别:1.submit(Callable task)、submit(Runnable task, T result)、submit(Runnable task)归属于ExecutorService接口。 2.execute(Runnable command)归属于Executor接口。ExecutorService继承了Executor。submit()有返回值。 execute没有返回值submit()方便做异常处理。通过Future.get()可捕获异常。 60、什么是阻塞式方法?
java中的阻塞式方法是指在程序调用改方法时,必须等待输入数据可用或者检测到输入结束或者抛出异常,否则程序会一直停留在该语句上,不会执行下面的语句。
java中很多方法都是属于阻塞式方法,比如InputStream中的read()方法和多线程中sleep()方法等。
61、Java 中的 ReadWriteLock 是什么?
答:读写锁是用来提升并发程序性能的锁分离技术的成果。Java中的ReadWriteLock是Java 5 中新增的一个接口,一个ReadWriteLock维护一对关联的锁,一个用于只读操作一个用于写。在没有写线程的情况下一个读锁可能会同时被多个读线程 持有。写锁是独占的,你可以使用JDK中的ReentrantReadWriteLock来实现这个规则,它最多支持65535个写锁和65535个读 锁。
62、volatile 变量和 atomic 变量有什么不同?
这是个有趣的问题。首先,volatile 变量和 atomic 变量看起来很像,但功能却不一样。Volatile变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性。例如用volatile修饰count变量那么 count++ 操作就不是原子性的。而AtomicInteger类提供的atomic方法可以让这种操作具有原子性如getAndIncrement()方法会原子性的进行增量操作把当前值加一,其它数据类型和引用变量也可以进行相似操作
- 63、可以直接调用 Thread 类的 run ()方法么?
而如果直接用Run方法,
这只是调用一个方法而已,
程序中依然只有主线程–这一个线程,
其程序执行路径还是只有一条,
这样就没有达到写线程的目的。
64、如何让正在运行的线程暂停一段时间?
1.可以使用Thread类的Sleep()方法让线程暂停一段时间。需要注意的是,这并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable,并且根据线程调度,它将得到执行。2.可以使用thread.wait()方法来让线程暂停一段时间,wait方法里面填写的是暂停的时间的长度,一毫秒为单位,
65、你对线程优先级的理解是什么?
1.package countPriority;1. 线程优先级用thread.setPriority(int a)( 1<=a<=10)方法来进行赋值2. * 线程优先级有继承性,如果主线程启动threadA线程且threadA线程没有另外赋予优先级,则threadA线程优先级和main线程一样。优先级与执行顺序无关3. * CPU尽量将执行资源让给线程优先级高的,即线程优先级高的总是会大部分先执行,但是不代表高优先级的线程全部都先执行完再执行低优先级的线程4. * 优先级具有随机性,优先级高的不一定每次都先执行5. * 本例中用一个变量的自增量来判断两个优先级不同的线程运行时哪个占用CPU资源多
66 、 什 么 是 线 程 调 度 器 (Thread Scheduler) 和 时 间 分 片 (TimeSlicing )?
1.线程调度器:是一个操作系统服务,它负责为 Runnable 状态的线程分配 CPU 时间。
一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。同上一个问题,线程调度并不受到 Java 虚拟机控制,所以由应用程序来控制它是 更好的选择(也就是说不要让你的程序依赖于线程的优先级)2.时间分片是指将可用的 CPU 时间分配给可用的 Runnable 线程的过程。分配 CPU 时间可以基于线程优先级或者线程等待的时间
67、你如何确保 main()方法所在的线程是 Java 程序最后结束的线程?
1、java中线程的结束是由run方法运行完成后自动结束的
2、在main线程(主线程)中,需要得到所有线程的引用。
3、知道jdk提供的CountDownLatch的用法
68、线程之间是如何通信的?
当前线程在别的线程调用notify(),notifyAll()方法前,导致当前线程等待.
线程调用wait(),释放其对锁的拥有权,等待另外的线程唤醒他.
为了确保wait的时候拥有锁,wait方法必须放在sychronized中.最后,使用生产消费模式实现线程之间的通信。
69、为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在Object 类里?Java的每个对象中都有一个锁(monitor,也可以成为监视器) 并且wait(),notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在Java的线程中并没有可供任何对象使用的锁和同步器。
这就是为什么这些方法是Object类的一部分,这样Java的每一个类都有用于线程间通信的基本方法
70、为什么 wait(), notify()和 notifyAll ()必须在同步方法或者同步块中被调用?
我们常用wait(),notify()和notifyAll()方法来进行线程间通信。线程检查一个条件后就行进入等待状态,例如,在“生产者-消费者”模型中,生产者线程发现缓冲区满了就等待,消费者线程通过消费一个产品使得缓冲区有空闲并通知生产者线程。notify()或notifyAll()的调用给一个或多个线程发出通知,告诉它(它们)条件已经发生改变,并且,一旦通知线程离开同步块,所有等待这个对象锁的线程将竞争这个对象锁,幸运的线程获得锁后就从wait()方法返回并继续执行。让我们把这整个操作分成几步来看看wait()和notify()方法之间的竞争条件(race condition),我们将使用“生产者-消费者”模型以便更容易理解这个场景:
生产者线程测试条件(缓冲区是否已满)并确定必须等待(发现缓冲区满后)
消费者线程从缓冲区消费一个产品后设置条件
消费者线程调用notify()方法,由于生产者线程此时还没有等待,这个消息将被忽略。
生产者线程调用wait()方法并进入等待状态。
因此,由于这里的竞争条件,我们可能在丢失一个通知,如果我们使用缓冲区或者只有一个产品,生产者线程将永远等待,你的程序也就挂起了。现在我们考虑下这个潜在的竞争条件怎么解决。可以通过使用Java提供的synchronized关键字和锁来解决这个竞争条件。为了调用wait(),notify()和notifyAll()方法,我们必须获取调用这些方法的对象上的锁。由于wait()方法在等待前释放了锁并且在wait()方法返回之前重新获得了锁,我们必须使用这个锁来确保检查条件(缓冲区是否已满)和设置条件(从缓冲区取产品)是原子的,而这可以通过同步块或者同步方法实现。简而言之,我们从同步块或者同步方法中调用wait(),notify()和notifyAll()方法可以避免:
IllegalMonitorStateException,如果我们不通过同步环境(synchronized context)调用这几个方法,系统将抛出此异常
wait()和notify()之间任何潜在的竞争条件。
71、为什么 Thread 类的 sleep()和 yield ()方法是静态的?
1.sleep()方法暂停当前线程后,会给其他线程执行机会,线程优先级对此没有影响。yield()方法会给优先级相同或更高的线程更高的执行机会。 2.sleep()方法会将线程转入阻塞状态,直到阻塞时间结束,才会转入就绪状态。yield()方法会将当前线程直接转入就绪状态。 3.sleep()方法声明抛出了InterruptedException异常,所以调用sleep()方法时要么捕捉该异常,要么显示声明抛出该异常。yield()方法则没有声明抛出任何异常。4.sleep()方法比yield()方法有更好的移植性,通常不建议使用yield()方法来控制并发线程的执行。
72、如何确保线程安全?1.对非安全的代码进行加锁控制;2.使用线程安全的类;3.多线程并发情况下,线程共享的变量改为方法级的局部变量。