Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块(synchronized) 和 volatile 关键字机制。
synchronized
在多线程访问的时候,同一时刻只能有一个线程能够用访问synchronized修饰的代码块。
举例
/*
* 创建两个线程,使用一个公共资源count,每一个线程在执行run方法是会输出名称和count的值,因为java线程具有随机性,因此输出的内容顺序会乱
* 输出结果:
* thread1:0
* thread1:2
* thread1:3
* thread1:4
* thread1:5
* thread2:1
* thread2:6
* thread2:7
* thread2:8
* thread2:9
*/
public class Synchronized implements Runnable{
//共享资源变量
int count = 0;
public static void main(String[] args) {
// TODO Auto-generated method stub
Synchronized synchronizedTest = new Synchronized();
Thread thread1 = new Thread(synchronizedTest,"thread1");
Thread thread2 = new Thread(synchronizedTest, "thread2");
thread1.start();
thread2.start();
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+count++);
}
}
}
使用了synchronized关键字修饰run方法后,输出结果就是正常的,保证同一时间内就一个线程执行。
/*输出结果:
*thread1:0
*thread1:1
*thread1:2
*thread1:3
*thread1:4
*thread2:5
*thread2:6
*thread2:7
*thread2:8
*thread2:9
*/
public synchronized void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+count++);
}
}
volatile
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值(都强制从主内存中取值,而不是自己的工作内存),即保证数据的一致性。
举例
/*
* 该程序首先创建一个Volatile类对象,然后执行run函数,之后线程休眠2秒,修改标志位,查看最后能否跳出while循环并输出
* 结果是无法跳出循环,表明volatileTest.flag = true;不起作用
*/
public class Volatile extends Thread{
boolean flag = false;
int i = 0;
public void run() {
while (!flag) {
i++;
}
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Volatile volatileTest = new Volatile();
volatileTest.run();
Thread.sleep(2000);
volatileTest.flag = true;
System.out.println("stop " + volatileTest.i);
}
}
上面程序无法跳出循环的原因是执行的线程为volatileTest,但是标志位是在主线程(main)中修改,因此在volatileTest线程中没有更新。
使用volatile关键字修饰flag变量后,就可以跳出循环,表示赋值成果(每次都从主内存取值)。
volatile boolean flag = false;