线程同步
线程安全问题:
如何解决:当一个线程a在操作某一资源时,其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作资源。这种情况即使线程a出现了阻塞,也不能被改变。
在Java中,我们通过同步机制,来解决线程的安全问题。
同步代码块
同步代码块的格式:
Synchronized(同步监视器)
{
需要被同步的代码;
}
- 这个同步监视器(锁)可以由object或this来填充,
- 同步生效的前提是程序的多个线程共用这同一把锁
- Synchronized确保了同一时刻只有一个对象能运行同步代码块中的代码。
同步方法
使用Synchronized作为修饰符进行修饰的函数,同样可以做到同步代码块的效果。
则这个方法运行时,即为synchronized的
同步函数的锁有两种情况:
1) 非静态函数:所采用的锁是this
2) 静态函数:所采用的锁是该函数所属字节码文件对象,可以使用obj.getClass()或 class_name.class 获取。
Lock锁
Java还可以通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
- ReentrantLock类实现了Lock,它拥有与 synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是 ReentrantLock,可以显式加锁、释放锁。
步骤:
1、实例化一个锁
private ReetrantLock lock = new ReetrantLock();
2、加锁(在该方法后面的代码相当于被加了synchronized)
lock.lock();
3、解锁
lock.unlock();
线程间通讯
线程间通讯方法:
- wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
- notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
- notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
注意:这些方法都必须定义在同步中,因为这些方法都用于操作线程状态的方法。
因为必须要明确到底操作的是哪个锁上的线程(进来的线程是那个,锁的对象就是哪个)
线程间通讯使用场景:
多个线程在处理统一的资源,但是任务却不同。
生产者消费者模型
版本v0.1_单实例生产者消费者模型
采用方法:synchronized、obj.notify、obj.wait
这样解决了单实例(一个生产者一个消费者的问题)
版本v0.2_多实例生产者消费者模型
当多个生产者消费者时,由于此前使用的notify(),具有随机性
所以当使用的是if判断时,我们会发现,当需要唤醒输出方,却把本方线程唤醒了
就会导致线程安全问题
版本v0.3_while+notifyAll循环版
- 由于if只判断一次,线程苏醒时,不需要再次判断,就会导致线程安全问题,而使用while,苏醒后,继续判断flag,就解决了问题
notify由于其不确定性,改为使用notifyAll,虽然会使得所有线程都苏醒,但配合while就没啥影响
总结:
wait(),notify(),notifyAll(),用来操作线程为什么定义在Object类中?而不是所属的类(Thread)中?
- 这些方法存在与同步中:
- 使用这些方法时必须要标识所属的同步的锁。
- 锁可以是任意对象,所以任意对象调用的方法一定定义Oblect类中