前言
多线程编程可以极大地提高了效率,但也会带来线程安全问题。比如说多个线程向数据库插入数据,就可能会导致数据库中数据重复。
什么时候会引发线程安全问题
首先我需要了解什么是临界资源?有这样一种资源,在某一时刻只能被一个线程所使用,这种资源可以是各种类型的的资源:一个变量、一个对象、一个文件、一个数据库表等。
举个简单的例子:
现在有两个线程分别从网络上读取数据,然后插入一张数据库表中,要求不能插入重复的数据。那么必然在插入数据的过程中存在两个操作:
1)检查数据库中是否存在该条数据;
2)如果存在,则不插入;如果不存在,则插入到数据库中。假如两个线程分别用thread-a和thread-b表示,某一时刻,thread-a和thread-b都读取到了数据X,那么可能会发生这种情况:thread-a去检查数据库中是否存在数据X,然后thread-b也接着去检查数据库中是否存在数据X,如果数据x不存在,就会导致重复插入。
如何解决线程安全问题
基本上所有的并发模式在解决线程安全问题时,都采用“序列化访问临界资源”的方案,即在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问。通常来说,是在访问临界资源的代码前面加上一个锁,当访问完临界资源后释放锁,让其他线程继续访问,即通过加锁实现
synchronized同步方法或者同步块
在了解synchronized关键字的使用方法之前,我们先来看一个概念:互斥锁,顾名思义:能到达到互斥访问目的的锁。举个简单的例子:如果对临界资源加上互斥锁,当一个线程在访问该临界资源时,其他线程便只能等待。 在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。
在Java中,可以使用synchronized关键字来标记一个方法或者代码块,当某个线程调用该对象的synchronized方法或者访问synchronized代码块时,这个线程便获得了该对象的锁,其他线程暂时无法访问这个方法,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁,其他线程才能执行这个方法或者代码块。
下面通过几个简单的例子来说明synchronized关键字的使用:
1.synchronized方法