目录
1.1 线程间通信的方式有哪些
-
传统的线程通信
使用synchronized 修饰的同步方法或者同步代码块,使用Object类下的wait() notify() notifyAll() 方法控制线程通信
wait() 导致该线程阻塞等待其他线程使用notify() 方法唤醒
notify()唤醒在此同步监视器的某一个线程
notifyAll() 唤醒此同步监视器的全部的线程 -
使用Condition
使用Lock 当作同步的对象,由于没有隐式的监视器,使用Condition类的await() signal() signalAll()方法实现通信 -
使用组赛队列
BlockingQueue 实现消费者生产者
put() 尝试往队列里面放入元素,满了就会阻塞
take() 尝试拿出元素 空就阻塞
BlockingQueue实现生产者 消费者
public class BlockingQueueTest3 {
class Producer extends Thread{
public BlockingQueue<String> bq;
public Producer(BlockingQueue<String> queue){
this.bq = queue;
}
@Override
public void run(){
String product_name = Thread.currentThread().getName();
System.out.println("我已经生产了"+Thread.currentThread().getName());
try {
bq.put(product_name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer extends Thread{
BlockingQueue<String> bq;
public Consumer(BlockingQueue<String> bq){
this.bq = bq;
}
@Override
public void run(){
try {
String product_name = bq.take();
System.out.println("消费了:"+product_name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
BlockingQueue<String> bq = new LinkedBlockingDeque<>(1);
BlockingQueueTest3 bb = new BlockingQueueTest3();
for (int i = 0; i < 5; i++) {
bb.new Producer(bq).start();
bb.new Consumer(bq).start();
}
}
}
1.2 HashMap,ConcurrentHashMap
1.3 synchronized加到普通方法和静态方法的区别
static 获得的是类的锁,非静态获得到的是实例的锁
不会产生互斥
1.4 一个类的两个方法都加了synchronized,是一个锁还是两个锁;
一个,同一个对象锁只能被一个线程占用
1.5 volatile
volatile 的作用:
- 保证被volatile修饰的共享变量对所有线程总是可见的,也就是当一个线程修改了一个被volatile修饰共享变量的值,新值总是可以被其他线程立即得知。
- 加了CPU的内存屏障指令,禁止指令重排序优化
volatile 如何做到可见性:
我们知道处理器速度与主存速度是不一致的,每个cpu自己维护一个处理缓存,当两个线程修改同一份数据的时候就会出现数据不一致的情况,加上volatile 关键字后,相当于加了一个内存屏障指令,然后:
- 当线程修改这个变量的时候会立即刷新到主内存
- 当读取该变量的时候会把该线程对应的本地内存设置无效,然后去主内存读取。
1.6 synchronized
可以修饰成员方法(实例) 静态方法(类对象) 代码块(括号后面的对象)
先用javac Test.class
编译出.class文件
然后用javap -c Test.class
查看字节码
- 修饰对象是基于 进出
monitor
对象实现 - 修饰方法是基于
ACC_SYNCHRONIZED
修饰,表示方法加锁了。它会在常量池中增加这个一个标识符,获取它的monitor
,所以本质上是一样的。
实现锁的状态管理是在对象头中的mark work
锁的四种状态:
- 偏向锁
一个线程每次进出方法都要解锁加锁,这样会造成资源浪费,所以引出偏向锁,在线程第一次获得锁的时候就在对象头中的锁标志位标注位偏向锁 - 轻量级锁
当线程A获得偏向锁后,线程B进入竞争状态,需要获得线程A持有的锁,那么线程A撤销偏向锁,进入无锁状态。线程A和线程B交替进入临界区,偏向锁无法满足,膨胀到轻量级锁,锁标志位设为00。 - 重量级锁
当多线程交替进入临界区,轻量级锁hold得住。但如果多个线程同时进入临界区,hold不住了,膨胀到重量级锁
1.7 volatile与synchronized区别和使用场景
- volatile 是一种轻量级锁,synchronized是重量级锁(悲观锁)
- 对于多线程 volatile 不是互斥的关系,synchronized 是互斥的,
- volatile不能保证变量状态的“原子性操作”
1.8
未完待续