一,sychoronized
synchronized 本质就是获取当前实例的对象锁
对象锁 其实就是一个标记,同一时刻只能被一个线程持有,线程要想访问使用synchronized 修饰的方法,必须先持有对象锁
一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。
同步方法
权限修改符 sychoronized 返回值类型 方法名(参数){
// 代码
}
同步代码块
权限修改符 返回值类型 方法名(参数){
synchoronized(对象){
代码
}
}
二,死锁
死锁:其实就是两个线程互相等待对方持有的锁 ======》两个线程都阻塞在这里无法继续执行
例:
//创建两个对象
Object objectA = new Object();
Object objectB = new Object();
//分别创建线程持有A和B
Runnable runnable = new Runnable() {
@Override
public void run() {
//使用syn修饰代码块持有A
synchronized (objectA){
System.out.println("当前线程持有A");
//休眠两秒,使其不抢占B
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//在持有A的情况下去持有B
synchronized (objectB){
System.out.println("当前线程持有B");
}
}
}
};
//赋值上面代码块做修改
Runnable runnable01 = new Runnable() {
@Override
public void run() {
//使用syn修饰代码块持有B
synchronized (objectB){
System.out.println("当前线程持有B");
//休眠两秒,使其不抢占A
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//在持有B的情况下去持有A
synchronized (objectA){
System.out.println("当前线程持有A");
}
}
}
};
//创建两个线程分别运行
Thread thread = new Thread(runnable);
Thread thread01 = new Thread(runnable01);
thread.start();
thread01.start();
由运行结果得知,两个线程在分别持有AB后便陷入死锁,等待对方释放另一个对象锁
在创建多线程时应避免出现死锁的情况
三,线程间的通信
在某些情况下,某个持有对象锁的线程不满足执行条件时,需要让该线程等待并释放持有的对象锁,让其他线程获取锁,直到条件满足并且其他线程会唤醒该线程
线程等待与唤醒
等待
public final void wait()
public final void wait(long time)
唤醒
public final void notify()
public final void notifyAll()
等待和唤醒必须成对出现
必须先持有对象锁
必须在同步代码块,同步代码方法内部使用
以生产者消费者模型为例
//首先创建一个库(类名为Tx02)用于储存生产者生产的商品,供给消费者消费
//货物进出以链表形式进行
private LinkedList<String> LiS = new LinkedList<>();
//设定库的容量为20
private int volume = 20;
//构造生产者存储货物的方法 使用syn修饰 存储与消费不能并发执行 所以 货物只能被一个线程同时持有
public synchronized void addVolume(String cargo) throws InterruptedException {
//生产需要时间
Thread.sleep(100);
//如果容量已满 生产者队列需要等待
if (LiS.size()>=20){
System.out.println("当前仓库容量已满");
wait();
}
//否则货物添加进入库中
LiS.add(cargo);
//唤醒等待中的消费者队列
notify();
}
/**
* 消费者队列
*/
public synchronized String pullVolume () throws InterruptedException {
//消费者需要时间消费
Thread.sleep(200);
//如果库中没有商品,消费者队列等待
if (LiS.size()==0){
System.out.println("当前库已空 消费者进入等待");
wait();
}
notify();
//从队列获取商品
return LiS.poll();
}
接下来创建测试
//创建库
Tx02 tx02 = new Tx02();
//生产者
Runnable runnableAdd = new Runnable() {
@Override
public void run() {
//循环200次增加货物
for (int i = 0; i < 200; i++) {
try {
tx02.addVolume(i+" ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
//消费者
Runnable runnablepull = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
try {
//展示消费者获得的数据
String Str = tx02.pullVolume();
System.out.println(Str);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
//将生产者于消费者放入线程中验证线程等待与唤醒
Thread threadadd = new Thread(runnableAdd);
Thread threadpull = new Thread(runnablepull);
//启动线程
threadadd.start();
threadpull.start();