众所周知,在编程的过程中不可避免的会用到多线程,既然有多线程就会考虑到互斥问题,那么我们来看一下Java多线程中的互斥。
下面先举个多线程容易出现的问题。比如,你的银行账户中有1000元,现在你正在网上银行正在给朋友转账(200元),但是同时有朋友在给你转账(500元),当你正要进行转账的前一刻,朋友首先转账成功,那么此时你账户中的钱应该是原来的钱加上朋友转账的钱,即1000+500=1500元,但是此时你正在转账的时刻已经取得了银行账户的目前余额(1000元),然后你按下转账按钮,此时计算账户余额时是使用之前的余额(1000元)减去转出的钱(200元)等于800元,账户重置成800,那这个问题就比较严重了。所以在多线程中,有些片段的执行时需要保持原子操作的,即在此片段执行完成之前其他线程不能来执行该片段,那么这就要用到多线程的互斥。
下面先看如下代码:
package com.alex.lip.iteye.traditional.synchronizeded;
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init() {
OutPuter outputer = new OutPuter();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("liping");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("weixiaoshan");
}
}
}).start();
}
class OutPuter {
/**
* 本方法是未使用同步
*
* @param name
*/
public void output(String name) {
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
运行结果:
beijing
shanghai
beijing
shanghai
beijing
shanghai
beijshanghai
ing
shanghai
beijing
在此可以看出,会出现如上的结果,证明在第一个线程执行中cpu就切换执行第二个线程,出现打印数据混乱,虽然是小概率事件,但是却不容忽视,那么怎么改正呢?从运行结果可以看出,程序在打印输出阶段出现共同调用一段代码,即
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
出现问题,那么我们可以在这一段加锁,使这一段代码只能被一个线程访问即可,那么但是我们要把锁加在哪个位置呢?首先我们可以锁定outputer对象,因为两个线程是使用同一个对象来调用该方法,所以我们可以做如下改正:
/**
* 本方法是使用同步代码块,锁定本类的对象
*
* @param name
*/
public void output1(String name) {
int length = name.length();
synchronized (this) {
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
this代表当前对象,此时就避免了多个线程同时访问该方法,既然该代码块是要同步的,那么我们是否可以给整个方法加上同步呢?答案是可以的,看如下代码:
/**
* 本代码使用同步方法,方法上的synchronized关键字锁定的是this对象
*
* @param name
*/
public synchronized void output2(String name) {
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
,假如我们的类是静态的类又如何解决呢?大家都知道,静态类是可以直接调用其中的方法的,不需要创建对象即可,但是静态类也总是要编译后运行的,那么它总要生成对应的字节码,即class文件,所以我们加锁时可以锁定静态类的class即可:
public void output1(String name) {
int length = name.length();
synchronized (OutPuter.class) {
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}