1、CountDownLatch和CyclicBarrier的区别?
1)CountDownLatch作用允许一个线程等待其他线程执行完成后,才执行。而另外一个允许N个线程相互等待到某个公共屏障点,然后这组线程再同时执行。
2)CountDownLatch计数器的值无法被重置,这个初始值只能被设置一次,不能重用,而第二个的计数器可以重用。
2、Semaphore可以控制某个资源可被同时访问的个数,通过构造函数设定一定数量的许可,通过acquire()获取
3、阻塞队列种类
1)ArrayBlockingQueue是一个由数组支持的有界缓存的阻塞队列,在读写操作上都需要锁住整个容器,因此吞吐量与一般的实现相似,是线程安全的,生产者和消费者公用一把锁。
2)LinkedBlockingueue是一种基于链表的阻塞队列,内部维持着一个数据缓冲队列,只有当队列缓冲区达到最大值缓存容量时,才会阻塞生产者线程,生产者端和消费者端采用独立的锁来控制数据同步
3)上面二者区别是:a、队列大小初始化方法不同,ArrayBlockingQuenue必须指定队列大小。LinkedBlockingQueue 有一个默认大小。
b、队列锁实现不同。
c、在生产或消费时操作不同 (前者基于数组,直接将对象插入或删除,后者基于链表,需要转为 Node<E>进行删除或插入,生成一个额外的Node对象)
4)PriorityBlockingQueue
为基于数组的无界阻塞队列,会按照优先级对元素进行排序,按照优先级顺序出队。每次出队元素都是优先级最高 的。但是队内元素不是按照优先级排列。
5)DelayQueue(缓存系统设计,缓存中对象超过有效时间,使用其查询,表示缓存时间已到)
是一个无界阻塞队列,用于放置实现了Delayed接口的1对象,只有在延迟期才能从中提取元素。优先队列的比较基准 值是时间。
6)SynchronousQueue
同步队列是一个不存储元素的队列,缓存值为1的阻塞队列。
4、如果不用锁机制如何实现共享数据访问。
无锁化编程的方法:CPU同步原语CAS,如:无锁栈、无锁队列;Atomic包;AtomicInteger是一个支持原子操作的Integer类。
CAS优缺点:保证了原子性,不会锁住当前线程;
ABA问题:CAS在操作值得时候,如果值没有变化情况下才会更新,但是如果A,变为B,又变为A,那么CAS 进行检查时,会认为没有变化。
CAS需要取出内存中某个时刻的数据,在下一时刻取出后的数据与原始数据比较替换,在这个时间差内导致数 据变化。
循环时间长开销大。
只能保证一个共享变量的原子操作。
5、java中实现多线程的四种方式?
1)继承Thread类创建线程类(重写run方法)
2)通过Runnable接口创建线程类。(重写run方法,创建Runnable实现类的实例,调用start启动)
3)通过Callable和Future创建线程
4)通过线程池创建线程(线程可复用,利用Executors创建线程池)
6、java中Runnable和Callable有什么不同?
1)Callable定义方法为call(),Runnable中为run()
2)Callable的call方法有返回值,run没有返回值
3)call方法可以抛出异常,run方法不能抛出异常
7、实现线程同步
1)synchronized的用法(同步代码块)
这个方法中的内容不能被多个线程访问。
修饰非静态方法
用synchronized修饰非静态方法,会将对象上锁。
修饰静态方法
会对这个方法所在类的Class对象上锁。
2)Lock用法(必须在try-catch-finally块中进行)
将释放锁的操作放在finally块中进行,以保证锁一定被释放,防止死锁的发生。
Lock和synchronized的区别:1、Lock是一个接口,而synchronized是java关键字,是内置语言实现。
2、synchronized在发生异常时会自动释放线程占有的锁,因此不对发生死锁,而Lock发生异 常时,需要释放锁操作。
3、Lock可以让等待锁的响应中断。
4、Lock有一个方法可以判断是否获取锁(tryLock()方法)
8、volatile和synchronized区别
1)volatile是变量修饰符,而synchronized作用于代码块或方法。
2)volatile不会对变量加锁,而synchronized会对变量加锁。
3)volatile仅能实现变量修改可见性,不能保证原子行,而synchronized可以保证变量修改可见性和原子性
4)volatile标记的变量不会被编译器优化,禁止指令重排序。
9、悲观锁和乐观锁
悲观锁:认为肯定有其他线程来争夺资源,因此不管到底会不会发生争夺,都会锁住资源,会导致其它所有需要锁的线程挂起。
乐观锁:每次不加锁,如果因为冲突失败就重试,直到成功为止。通常基于CAS原子指令来实现。
10、实现线程间的通信?
1)Object类中wait()\notify()\notifyAll()方法
2)用Condition接口
3)管道实现通信(管道只能在两个线程之间传递数据,只能实现单向发送)
4)使用volatile关键字
11、如何确保线程安全?
Synchronized、Lock、原子类、同步容器、并发容器、阻塞队列、同步辅助类(CountDownLatch,Semaphore,CyclicBarrier)
12、多线程优点和缺点?
1、充分利用cpu,避免cpu空转
2、程序响应快。
3、上下文切换的开销大
4、增加了资源消耗
5、编程更加复杂
13、多线程锁的种类
代码层次:同步锁、可重入锁、公平锁、读写锁
数据库层次:悲观锁、乐观锁、表锁、行锁、页锁
可重入锁(ReentrantLock和synchronized):如果当前线程已经获得某个监视器对象所持有的锁,那么该线程再该方法中调用另外一个同步方法也持有该锁。
可中断锁(可以中断的锁,Lock为可中断锁)
公平锁(synchronized为非公平锁,无法保证等待线程获取锁的顺序,而ReentrantLock和ReentrantReadWriteLock默认为非公平锁,但是可以设置为公平锁,尽量按请求锁的顺序获取锁)
14、锁优化
1、自旋锁
为了让线程等待,让线程执行一个忙循环。
2、锁清除
虚拟机即时编译编译器在运行时,对一些代码上要求同步,但检测到不可能存在共享数据竞争锁进行清除。
3、锁粗化
如果虚拟机探测到有一串零碎的操作都对同一对象加锁,将会把加锁同步范围扩大到整个操作序列外部。
4、轻量级锁
在代码进入同步快时,如果此同步对象没有被锁定,虚拟机将首先在当前线程中简历一个名为锁记录的空间,用于存储锁对象目前的Mark Word拷贝。
5、偏向锁。
目的在于消除无竞争情况下同步源于,进一步提高程序运行性能。
15、wait()和sleep()区别
1)来自不同类,sleep来自Thread类,是静态方法。wait()是Object类,实现线程通信。
2)sleep是将当前线程挂起指定的时间,没有释放锁。wait释放了锁。
3)wait只能在同步控制方法和同步控制块中。
16、java中interrupted和isInterrupted区别
前者为静态方法,后者为非静态方法,interrupted作用域当前正运行的线程,isInterrupted作用域调用该方法的线程对象所对应的线程。
前者会中断状态清除而后者不会。
17、java中start()和run()区别?
start()方法来启动线程,并在新线程中运行run()方法,真正实现多线程运行。
直接调用run()方法的话,会把run()当做普通方法调用,会在当前线程执行run()方法,而不会启动新线程运行
18、线程上下文切换?
对于单核CPU,只能在一个时刻运行一个线程,当在运行一个线程的时候去运行另一个线程叫上下文切换。
19、用户线程和守护线程
在用户线程调用start方法之前,调用对象的setDamon(true)方法,一个守护线程是在后台执行且不会阻止JVM终止的线程,作为为为其它线程运行提供便利服务,当没有用户线程在运行时,JVM关闭程序且退出。
20、什么是线程调度器
是一个操作系统服务,负责为Runnable状态的线程分配CPU时间。
21、线程状态
创建、就绪、运行、阻塞、死亡
22、有三个线程,如何确保他们按顺序执行
join方法(Thread类中的join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行)
23、主线程中,要求大量子线程执行完,在执行主线程。
1)join方法
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
2)CountDownLatch,一个同步辅助类,在完成一组正在其它线程中执行操作之前,允许一个或多个线程等待
3)使用线程池
24、ThreadLocal原理
相当于一个容器,用于存放每个线程的局部变量。实例通常来说都是private static类型,一般情况下,通过ThreadLocal.set到线程中的对象是该线程自己使用的对象,其它线程是访问不到的,各个线程中访问的是不同的对象,如果进去的东西本来是共享的对象,那么get到的依旧是共享对象本身。
向ThreadLocal中set的变量是由Thread线程对象自身保存的,当用户调用时,该方法则是通过currentThread获取当前线程,将变量存入线程ThreadLocalMap类中,Map中元素的键为当前threadlocal对象,而值对应线程中变量副本。
25、如何在两个线程中共享数据
一、每个线程执行代码相同
可以共同使用同一个Runnable对象。
二、代码不同
将共享数据封装为一个对象,将对象传递给不同代码的Runnable对象。