文章目录
一. synchronized简介
synchronized
用于并发编程中,可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块
二. 锁方法
先通过runnable实现一个简单的多线程,用synchronized
修饰increase
我们的目标是通过两个线程,使用 i++,让 i 增长至20000
public class SynchronizedTest implements Runnable {
static int i = 0;
public synchronized void increase() {
i++;
}
public int getI() {
return i;
}
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
increase();
}
}
}
我们在同时开启这两个线程运行,代码如下所示:
public static void main(String[] args) {
SynchronizedTest synchronizedTest = new SynchronizedTest();
Thread t1 = new Thread(synchronizedTest);
Thread t2 = new Thread(synchronizedTest);
t1.start();
t2.start();
try {
// join是阻塞主进程,直到线程结束再运行后面的代码
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(synchronizedTest.getI());
}
结果如下所示(I
一定为20000):
当我们取消掉synchronized
,此时SynchronizedTest
的代码如下所示:
public class SynchronizedTest implements Runnable {
static int i = 0;
public void increase() {//仅有此处不同,少了synchronized
i++;
}
public int getI() {
return i;
}
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
increase();
}
}
}
main
函数不变化,再次运行,执行结果如下所示(I
会变化):
说明在没有synchronized
的时候,线程可能同时执行increase
,使得
线程一的increase
:读取 i,i = i + 1
线程二的increase
:读取 i,i = i + 1
读取的 i 值相等,使得 i 没有正确的累加。
加了synchronized
之后,一个线程需要等待另外一个线程完成后,才能调用
increase
,这就使得 i 能够正确变为 20000
三. 代码块
3.1 锁变量(成员锁)
锁的对象是变量。
由于synchronized
的参数要求是object
,所以原本int i
属于基础类型,并不属于object
,此处需要将int
换成Integer
private static Integer i = 0;
public void increase() {//仅有此处不同,少了synchronized
synchronized (i) {
i++;
}
}
3.2 锁当前实例(实例对象锁)
this 代表当前实例
public void increase() {//仅有此处不同,少了synchronized
synchronized (this) {
i++;
}
}
3.3 锁当前类(Class对象锁)
public void increase() {//仅有此处不同,少了synchronized
synchronized (SynchronizedTest.class) {
i++;
}
}
四、线程安全的类
线程不安全 | 线程安全 | 其他区别 |
---|---|---|
HashMap | Hashtable | HashMap可以存放 null,Hashtable不能存放null |
StringBuilder | StringBuffer | StringBuilder拼接字符串更快(因为少了同步) |
ArrayList | Vector | 无 |
4.1 把非线程安全的集合转换为线程安全
ArrayList是非线程安全的,换句话说,多个线程可以同时进入一个ArrayList对象的add方法
借助Collections.synchronizedList,可以把ArrayList转换为线程安全的List。
与此类似的,还有HashSet,LinkedList,HashMap等等非线程安全的类,都通过工具类Collections转换为线程安全的
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = Collections.synchronizedList(list1);
}