多线程中的锁
编写多线程的程序是不可避免的,当我们需要在多线程的环境下编写一个安全的程序只有以下三个要点之一即可:
- 线程之间不共享数据
- 线程之间只共享不可变的数据
- 如果线程之间共享可变的数据,需要协调各个线程对共享数据的访问
前两个要求都好满足。
第一个,线程之间不共享数据,每个线程只访问自己的数据,不会影响其他线程,也不会被其他线程影响。java中的一个典型实现就是ThreadLocal这个类,为每个线程都可以存放一个自己线程对应的数据到这个类中,也只能从中取出自己的数据。
第二个,线程之间只共享不可变的数据,这个很好理解,所有线程获取到的数据都是一致的,也不可以对这个数据做任何改变,自然是线程安全的,比如说java中的枚举类型,天生就是线程安全的。
真正的难点在于第三个,这里简单介绍一下处理这个问题的基本工具:锁
锁的基本原理
如前所述,多线程的基本问题是协调对共享数据的访问。也就是说,在一个线程执行一个任务,访问一段数据的时候,其他线程不应该同时执行会影响它的动作。
这是一个经典的多线程问题例子,多线程卖票的问题
public static void main(String[] args) {
SellTicket task=new SellTicket();
new Thread(task).start();
new Thread(task).start();
}
static class SellTicket implements Runnable {
private int ticket = 10;
@Override
public void run() {
while (ticket>0){
ticket--;
System.out.println(ticket+"tickets left");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
单个线程在执行过程中,可能会出现在拿到ticket对它进行处理时,其他的线程也同时拿到了ticket,也对它进行了处理,这就出现了问题。出现问题的是
while (ticket>0){
ticket--;
System.out.println(ticket+"tickets left");
}
这一段代码,多个线程同时执行这几行代码出现了问题,我们想要同时只有一个线程能访问这些代码,其他线程想要执行这些代码等待这个线程执行完了这些代码后才可以。
我们知道,线程只能按照代码一行行机械的执行下去,各个线程的代码都是一致的,那怎么可以只让某个线程执行,而其他线程不执行呢?这就需要在执行前进行判断,判断哪些线程有权限执行下去,哪些没有权限执行下去。这个判断的依据就是锁,它是一个被多个线程访问的变量,它应该满足原子性和可见性。当访问这个变量时,它其中的数据表明你可以执行下去,那就表明你可以获得锁,你对它进行了一些操作,修改了一些数据,获得了锁。这是其他线程也来访问这个变量,它读取到的数据显示显示其他线程已经获取了这个锁,它不能继续往下执行了,它就在那里等待获取锁,进入了阻塞状态。
java中的锁
java语言中的synchronized提供了语言级的支持的锁,称为管程或者监视器(monitor),我们只需要在需要同步的代码块上使用synchronzied(obj)就可以控制对线程对这段代码的执行,达到同步的效果。
JUC工具包中还提供了多种类型的锁,如ReentrantLock ,ReentrantReadWriteLock 等,在不同情况下使用。
按照特性把锁分类可以参考这篇美团的博文,介绍的还算比较详细。
浙公网安备 33010602011771号