多线程-线程同步
线程同步
数据安全问题
出现问题的原因
- 多线程环境
- 共享数据
- 多条语句同时操作共享数据
数据安全问题的解决
思路:让程序没有安全问题的环境
同步代码块
可以用同步代码块将操作共享数据的多条语句锁起来
synchronized中文释义:同步
//任意对象
private Object obj = new Object();
synchronized (obj) {
//操作共享数据的语句
}
通过synchronized加锁后,被锁起来的语句不会同时被多个线程执行,这样就可以解决数据安全问题
注:不能用不同的obj作为‘锁’,那样就是用不同的‘锁’来控制代码块,会不起作用
使用实现Runnable的方式时,由于object与this都指代同一个对象,可直接使用,但使用继承Thread类方式时,不可直接使用object,实例化对象时需用静态对象
同步代码块的优缺点
- 好处:解决了多线程的数据安全性问题
- 当线程很多时,每个线程都会去判断同步代码块上面的‘锁’,这是一件很耗费资源的事,无形中会降低程序的运行效率
同步方法
同步方法就是将synchronized关键字加到方法上
-
格式:
修饰符 synchronized 返回值类型 方法名(方法参数){ }
同步方法的任意对象(obj)为本类(this)
同步静态方法
synchronized同样也可以加在静态方法上
-
格式:
修饰符 static synchronized 返回值类型 方法名(方法参数){ }
和同步方法不同,同步静态方法的任意对象(obj)为当前类的字节码文件(.class)
可通过类名.class的方式访问类的字节码文件
线程安全的类
StringBuffer
- 线程安全,可变的字符序列
- 从JDK5开始,被StringBuilder替代,如果不需要线程安全,更推荐使用StringBuilder
Vector
-
线程安全,该类实现了List接口
-
从Java2平台v1.2开始该类改进了List接口,是集合体系中的一员。Vector被同步,如果不需要线程安全,更推荐使用ArrayList
Hashtable
- 线程安全,该类实现了哈希表(键值对)
- 从Java2平台v1.2开始该类改进了Map接口,是集合体系中的一员。Hashtable被同步,如果不需要线程安全,更推荐使用HashMap
线程安全 | 非线程安全 |
---|---|
StringBuffer | StringBuilder |
Vector | ArrayList |
Hashtable | HashMap |
Collections.synchronized
除StringBuffer外,其他两个类我们并不常用到
在使用线程安全的集合时,我们通常使用Collections工具类的synchronized系列方法
public class ThreadDemo {
public static void main(String[] args) {
//将ArrayList集合变成线程安全的集合
List<Object> objects = Collections.synchronizedList(new ArrayList<>());
}
}
Lock锁
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比synchronized方法和语句更广泛的锁定操作
Lock接口提供了获得锁和释放锁的方法
- void lock():获得锁
- void unlock():释放锁
Lock是接口不能够实例化,可以用其实现类ReentrantLock实例化对象
- ReentrantLock():无参构造方法
public class ThreadDemo {
public static void main(String[] args) {
//考虑在声明lock的时候加入static和final关键字
ReentrantLock lock = new ReentrantLock();
try{
lock.lock();
//这里写被枷锁的代码块
}finally {
lock.unlock();
}
}
}
为了让unlock方法无论何时都被执行,这里使用try finally来实现
死锁
线程同步机制带来的问题:死锁
死锁不能由程序解开,只能是人为解锁
所以我们在编写代码时应该避免死锁的出现