1 synchronized
使用示例
synchronized
本质上要修改指定对象的
"
对象头
".
从使用角度来看
, synchronized
也势必要搭配一个具 体的对象来使用.
1)
直接修饰普通方法
:
锁的
SynchronizedDemo
对象
2)
修饰静态方法
:
锁的
SynchronizedDemo
类的对象
3)
修饰代码块
:
明确指定锁哪个对象
锁当前对象
锁类对象
充电理解:synchronized
锁的是什么
.
两个线程竞争同一把锁
,
才会产生阻塞等待
.
两个线程分别尝试获取两把不同的锁
,
不会产生竞争
2 wait()方法
wait
做的事情
:
使当前执行代码的线程进行等待
. (
把线程放到等待队列中
)
释放当前的锁
满足一定条件时被唤醒
,
重新尝试获取这个锁
.
wait
要搭配
synchronized
来使用
.
脱离
synchronized
使用
wait
会直接抛出异常
.
wait
结束等待的条件
:
其他线程调用该对象的
notify
方法
.
wait
等待时间超时
(wait
方法提供一个带有
timeout
参数的版本
,
来指定等待时间
).
其他线程调用该等待线程的
interrupted
方法
,
导致
wait
抛出
InterruptedException
异常
.
3 notify()
方法
notify
方法是唤醒等待的线程
.
方法
notify()
也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的
其它线程,对其发出通知
notify
,并使它们重新获取该对象的对象锁。
如果有多个线程等待,则有线程调度器随机挑选出一个呈
wait
状态的线程。
(
并没有
"
先来后到
")
在
notify()
方法后,当前线程不会马上释放该对象锁,要等到执行
notify()
方法的线程将程序执行
完,也就是退出同步代码块之后才会释放对象锁。
notifyAll()
方法
notify
方法只是唤醒某一个等待线程
.
使用
notifyAll
方法可以一次唤醒所有的等待线程
只修改这一部分其他不变,可自行运行代码看看
注意
:
虽然是同时唤醒
3
个线程
,
但是这
3
个线程需要竞争锁
.
所以并不是同时执行
,
而仍然是有先有后的执行.
wait
和
sleep
的对比
其实理论上
wait
和
sleep
完全是没有可比性的,因为一个是用于线程之间的通信的,一个是让线程阻 塞一段时间, 唯一的相同点就是都可以让线程放弃执行一段时间.
当然为了面试的目的,我们还是总结下:
1. wait
需要搭配
synchronized
使用
. sleep
不需要
.
2. wait
是
Object
的方法
sleep
是
Thread
的静态方法
.
4 标准库中的阻塞队列
在
Java
标准库中内置了阻塞队列
.
如果我们需要在一些程序中使用阻塞队列
,
直接使用标准库中的即可
.
BlockingQueue
是一个接口
.
真正实现的类是
LinkedBlockingQueue.
put
方法用于阻塞式的入队列
, take
用于阻塞式的出队列
.
BlockingQueue
也有
offer, poll, peek
等方法
,
但是这些方法不带有阻塞特性
.
生产者消费者模型
阻塞队列实现
通过
"
循环队列
"
的方式来实现
.
使用
synchronized
进行加锁控制
.
put
插入元素的时候
,
判定如果队列满了
,
就进行
wait. (
注意
,
要在循环中进行
wait.
被唤醒时不一
定队列就不满了
,
因为同时可能是唤醒了多个线程
).
take
取出元素的时候
,
判定如果队列为空
,
就进行
wait. (
也是循环
wait)
运行结果:
5 对比线程和进程
线程的优点
1.
创建一个新线程的代价要比创建一个新进程小得多
2.
与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
3.
线程占用的资源要比进程少很多
4.
能充分利用多处理器的可并行数量
5.
在等待慢速
I/O
操作结束的同时,程序可执行其他的计算任务
6.
计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
7. I/O
密集型应用,为了提高性能,将
I/O
操作重叠。线程可以同时等待不同的
I/O
操作。
6 进程与线程的区别
1.
进程是系统进行资源分配和调度的一个独立单位,线程是程序执行的最小单位。
2.
进程有自己的内存地址空间,线程只独享指令流执行的必要资源,如寄存器和栈。
3.
由于同一进程的各线程间共享内存和文件资源,可以不通过内核进行直接通信。
4.
线程的创建、切换及终止效率更高。