之前在Java内存模型与多线程中介绍过,在并发编程中,非常重要的两个问题是:通信和同步。
本博客主要对如何使用synchronized关键字实现线程间同步问题的自我学习总结。
1. 线程间的同步
同步:是用于控制不同线程间操作发生相对顺序的机制。
注:
- 有两个词语“线程安全”和“非线程安全”,同步保障了线程安全。因为非线程安全的发生,就是由于在
线程的创建和启动中提到的线程因其与生俱来的“随机性”造成的,所以如果线程的执行被控制为有序的,那么就不会有那么多麻烦了; - 非线程安全问题存在于共享变量中,即成员变量/实例变量,而不是方法内部的局部变量,因此只有共享资源的读写访问才需要同步。
2. synchronized关键字的使用
- 修饰方法,可以修饰普通的实例方法,或者静态的类方法;
- 修饰代码块;
例子: 创建3个线程,对变量num进行有序+1操作,循环100次。
代码实例1:
/*
* 不使用synchrinized关键字
*/
public class Test_synchronized implements Runnable {
private int num = 0;
@Override
public void run() {
printNum();
}
public void printNum(){
for(int i=0; i<100; i++){
num++;
System.out.println(Thread.currentThread().getName()+": "+num);
}
}
public static void main(String[] args) {
Test_synchronized r = new Test_synchronized();
for(int i=0; i<3; i++){
Thread t = new Thread(r);
t.start();
}
}
}
运行结果1:
……中间打印省略……
其中num
是共享变量,所以3线程同时对该变量进行读写时,在没有同步的情况下,是非线程安全的,
用关键字synchronized
修饰方法printNum()
后,代码和运行效果如下:
代码实例2:
/*
* 使用synchrinized关键字修饰实例方法printNum()
*/
public class Test_synchronized implements Runnable {
private int num = 0;
@Override
public void run() {
printNum();
}
synchronized public void printNum(){
for(int i=0; i<100; i++){
num++;
System.out.println(num);
}
}
public static void main(String[] args) {
Test_synchronized r = new Test_synchronized();
for(int i=0; i<3; i++){
Thread t = new Thread(r);
t.start();
}
}
}
运行结果2:
……中间打印省略……
共享变量num实现了有序递增。