您的位置:首页 > 编程语言 > Java开发

JAVA多线程编程---volatile与synchronized

2014-03-15 23:45 399 查看
多线程编程在项目中的应用极为重要,深刻理解JAVA在多线程的实现原理,可以有效解决各种并发问题。

在多线程编程中,经常会用到关键字volatile和synchronized,下面将详细说一下这两个关键字的作用和区别。

1、volatile

在JAVA内存模型中,分为主内存(main memory or stack memory)和工作内存(working memory),主内存保存着JAVA的实例变量、静态变量和数组元素;每个线程都有自己的工作内存,工作内存由缓存和栈组成,其中缓存保存着主存中变量的拷贝,栈保存着线程中的局部变量。线程之间无法直接通信,必须通过主内存进行通信。

下图说明了两个线程进行通信的过程,假设主内存有共享变量i,线程A和线程B如果都调用到了主内存的共享变量i,则线程A和线程B的工作内存都会保存这个共享变量i的拷贝,线程A在工作内存中修改了共享变量i后,会将其同步到主内存,再给线程B去调用。



volatile的作用是:告诉JAVA虚拟机,使用volatile修饰的变量,只会存在主内存中,工作内存中并不会保存它的拷贝。

volatile变量只能保证变量的可见性,即只存在主内存中,但无法保证变量修改的原子性,下面我们举一个例子来说明。

public class ThreadA extends Thread {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100000; i++) {
Example.add();
}
System.out.println("Thread A finished");
}
}


public class ThreadB extends Thread {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100000; i++) {
Example.add();
}
System.out.println("Thread B finished");
}
}


public class Example {
public static volatile  int count = 0;
public static void  add(){
count = count + 1;
}

public static void main(String[] args) {
new ThreadA().start();
new ThreadB().start();

try {
Thread.sleep(5*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("count="+Example.count);
}
}

在上面的例子中,我们让两个线程对共享变量count都自增100000次,期望的结果是200000,但程序输出的结果是:

Thread A finished
Thread B finished
count=153167

与我们的期望是不一致的,可以volatile无法保证volatile变量修改的原子性。惹要保持变量的数据同步,则在方法中加入synchronized则可。如下:

public class Example {
public static volatile  int count = 0;

public synchronized static void  add(){
count = count + 1;
}

public static void main(String[] args) {
new ThreadA().start();
new ThreadB().start();

try {
Thread.sleep(5*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("count="+Example.count);

}

}

程序输出的结果是:

Thread A finished
Thread B finished
count=200000


2、synchronized

synchronized 用来锁住一个方法、代码块、对象和类,被锁住的内容同时最多被一个线程访问。

2.1 锁住类

synchronized 关键字实现锁住类的方式有两种,一种是锁住静态方法,一种是锁住类变量,两种方式的作用一样,当锁住一个类时,该类所有同步的静态方法或代码块都会被锁住,但不会锁住非同步方法或同步的非静态方法。

2.1.1 锁住静态方法

public class ThreadExampleA extends Thread {

public ThreadExampleA(String name){
super.setName(name);
}

@Override
public void run() {
SynchronizedExample.lockMethodA(this);
}
}


public class ThreadExampleB extends Thread {
public ThreadExampleB(String name){
super.setName(name);
}

@Override
public void run() {
SynchronizedExample.lockMethodB(this);
}
}


public class SynchronizedExample {
public synchronized static void lockMethodA(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodA:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public synchronized static void lockMethodB(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodB:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public static void main(String[] args) {
new ThreadExampleA("A").start();
new ThreadExampleB("B").start();
}
}

上面的例子我们使用两个线程来访问类SynchronizedExample 中的两个静态方法,程序输出的结果是:

lockMethodA:A:1
lockMethodA:A:2
lockMethodA:A:3
lockMethodA:A:4
lockMethodA:A:5
lockMethodB:B:1
lockMethodB:B:2
lockMethodB:B:3
lockMethodB:B:4
lockMethodB:B:5

从程序的输出结果可以看出,两个同步的静态方法可以实现互斥,即当一线程访问类中的同步静态方法时,会给该类中的所有同步的静态方法加上锁,其他线程不能访问该类中同步的静态方法,一直到该锁被释放,但其他线程可以访问该类中的同步非静态方法或非同步静态方法,看以下例子:

public class ThreadExampleA extends Thread {

public ThreadExampleA(String name){
super.setName(name);
}

@Override
public void run() {
SynchronizedExample.lockMethodA(this);
}
}


public class SynchronizedExample {
public synchronized static void lockMethodA(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodA:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public synchronized void lockMethodB(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodB:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
new ThreadExampleA("A").start();
new ThreadExampleB("B", example).start();
}
}

程序的输出结果是:

lockMethodA:A:1
lockMethodB:B:1
lockMethodB:B:2
lockMethodA:A:2
lockMethodB:B:3
lockMethodA:A:3
lockMethodB:B:4
lockMethodA:A:4
lockMethodB:B:5
lockMethodA:A:5

看以下例子,锁住一个同步的静态方法,但线程可以访问其他非同步的静态方法。

public class SynchronizedExample {
public synchronized static void lockMethodA(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodA:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public static void lockMethodB(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodB:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public static void main(String[] args) {
new ThreadExampleA("A").start();
new ThreadExampleB("B").start();
}
}

程序输出的结果是:

lockMethodA:A:1
lockMethodB:B:1
lockMethodA:A:2
lockMethodB:B:2
lockMethodA:A:3
lockMethodB:B:3
lockMethodA:A:4
lockMethodB:B:4
lockMethodA:A:5
lockMethodB:B:5


2.1.2 锁住类变量

通过锁住类变量来锁住代码块,其作用与锁住静态方法相同,会同时锁住该类中的所有同步的静态方法或同步的静态代码块,但不会锁住非同步的静态方法或同步的非静态方法。

public class SynchronizedExample {
public synchronized static void lockMethodA(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodA:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public static void lockMethodB(Thread thread){
synchronized(SynchronizedExample.class){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodB:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}

public static void main(String[] args) {
new ThreadExampleA("A").start();
new ThreadExampleB("B").start();
}
}

程序的输出结果是:

lockMethodA:A:1
lockMethodA:A:2
lockMethodA:A:3
lockMethodA:A:4
lockMethodA:A:5
lockMethodB:B:1
lockMethodB:B:2
lockMethodB:B:3
lockMethodB:B:4
lockMethodB:B:5

2.2 锁住对象

synchronized锁住对象也有两种方法,一种是锁住非静态方法,另一种是锁住对象变量,两种方法的作用是一样的,可以实现互斥。如果synchronized用来锁住一个非静态方法或对象变量,则同时会锁住这个对象的所有同步的非静态方法和同步的代码块。

2.2.1 锁住非静态方法

public class SynchronizedExample {
public synchronized void lockMethodA(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodA:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public synchronized void lockMethodB(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodB:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
new ThreadExampleA("A", example).start();
new ThreadExampleB("B", example).start();
}
}

程序输出的结果是:

lockMethodA:A:1
lockMethodA:A:2
lockMethodA:A:3
lockMethodA:A:4
lockMethodA:A:5
lockMethodB:B:1
lockMethodB:B:2
lockMethodB:B:3
lockMethodB:B:4
lockMethodB:B:5

2.2.2 锁住对象变量

public class SynchronizedExample {
public synchronized void lockMethodA(Thread thread){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodA:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}

public void lockMethodB(Thread thread){
synchronized(this){
int i = 1;
while(i < 6){
try {
System.out.println("lockMethodB:"+thread.getName() + ":" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}

public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
new ThreadExampleA("A", example).start();
new ThreadExampleB("B", example).start();
}
}

程序输出的结果是:

lockMethodA:A:1
lockMethodA:A:2
lockMethodA:A:3
lockMethodA:A:4
lockMethodA:A:5
lockMethodB:B:1
lockMethodB:B:2
lockMethodB:B:3
lockMethodB:B:4
lockMethodB:B:5
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: