本本章位作者原创,转载请注明出处,欢迎大家一起交流,欢迎高手赐教,我的csdn博客地址是:http://blog.csdn.net/huo_chai_gun
线程的安全问题我们可以用银行转账来描述严重性:
假设一个人的账户有200块钱,这个人在网上买了一本hadoop权威指南 花了80块钱,那么有一个线程的程序运算位200-80,而偏偏此时另外一个人正在用余额宝给这个人转账300块钱,当第一个线程正在执行200-80的操作时,cpu切换到了另外一个人转账的线程,那么此时这个转账的线程就执行200+300的操作,执行完毕了,第一个买书的线程又继续执行,而这个时候这个线程取到的值还是200,所以买书的动作还是200-80,那么这个人最终的账户就有120块钱,相信通过这里例子我们可以看到问题的严重性了.
下面我们利用例子来看下线程安全的问题:
package org.yla;
/**
* 线程的安全问题
* @author huo_chai_gun
* 2014年12月10日下午11:54:45
*/
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init(){
final Outputer outputer = new Outputer();
//打印第1个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
outputer.printName("qiaodaima");
}
}
}).start();
//打印第2个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
outputer.printName("huo_chai_gun");
}
}
}).start();
}
//内部类
class Outputer{
//打印一个人的名字
public void printName(String name){
int length =name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
输出的结果:
huo_chai_gun
huo_chai_gun
huo_c
qiaodaima
qiaodaima
qiaodaima
qiaodaima
qiaodaimahai_gun
huo_chai_gun
上面的输出我们可以看出来线程的安全存在严重的隐患
思考?我们应该怎么解决上面的问题呢
修改下面的代码后我们看看运行效果:
package org.yla;
/**
* 线程的安全问题
* @author huo_chai_gun
* 2014年12月10日下午11:54:45
*/
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init(){
final Outputer outputer = new Outputer();
//打印第1个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
outputer.printName("qiaodaima");
}
}
}).start();
//打印第2个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
outputer.printName("huo_chai_gun");
}
}
}).start();
}
//内部类
class Outputer{
//打印一个人的名字
public void printName(String name){
int length =name.length();
<span style="color:#FF0000;">//括起来的代码表示独一无二的代码
synchronized(name){//但是这个()里面的东西必须使用的是同一个对象才可以</span>
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
<span style="color:#FF0000;">}</span>
}
}
}
程序运行结果:
qiaodaima
qiaodaima
qiaodaima
qiaodaima
ai_gun
huo_chai_gun
huo_chai_gun
没有达到我们想要的效果分析原因:
由于我们锁定的name对象,但是两个线程name是不一样,所以依旧会发生上面的问题
好,接下来我们再次修改代码:
package org.yla;
/**
* 线程的安全问题
* @author huo_chai_gun
* 2014年12月10日下午11:54:45
*/
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init(){
final Outputer outputer = new Outputer();
//打印第1个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
outputer.printName("qiaodaima");
}
}
}).start();
//打印第2个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
outputer.printName("huo_chai_gun");
}
}
}).start();
}
//内部类
class Outputer{
<span style="color:#FF0000;">String xxx="";</span>
//打印一个人的名字
public void printName(String name){
int length =name.length();
//括起来的代码表示独一无二的代码
synchronized(<span style="color:#FF0000;">xxx</span>){//但是这个()里面的东西必须使用的是同一个对象才可以
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
}
程序运行结果是我们想到的结果:
分析原因:我们锁定的对象时xxx字符串,而xxx字符串只有一个,达到的锁定的同一个对象,所以运行结果
接下来我们再次修改代码:
package org.yla;
/**
* 线程的安全问题
* @author huo_chai_gun
* 2014年12月10日下午11:54:45
*/
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init(){
final Outputer outputer = new Outputer();
//打印第1个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
<span style="color:#CC0000;">// outputer.printName("qiaodaima");
new Outputer().printName("qiaodaima");</span>
}
}
}).start();
//打印第2个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
<span style="color:#FF0000;">// outputer.printName("huo_chai_gun");
new Outputer().printName("huo_chai_gun");</span>
}
}
}).start();
}
//内部类
class Outputer{
String xxx="";
//打印一个人的名字
public void printName(String name){
int length =name.length();
//括起来的代码表示独一无二的代码
synchronized(xxx){//但是这个()里面的东西必须使用的是同一个对象才可以
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
}
程序运行结果又会出现线程安全问题:
分析原因:a、b两个线程分别调用自己的对象,也就是说锁定的xxx有两个,所以还是会出现问题
package org.yla;
import java.util.concurrent.TimeUnit;
/**
* 线程的安全问题
* @author huo_chai_gun
* 2014年12月10日下午11:54:45
*/
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init(){
final Outputer outputer = new Outputer();
//打印第1个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
outputer.printName("qiaodaima");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//打印第2个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while(true){
outputer.printName("huo_chai_gun");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
//内部类
class Outputer{
//打印一个人的名字
public void printName(String name){
int length =name.length();
//括起来的代码表示独一无二的代码
synchronized(<span style="color:#FF0000;">this</span>){//但是这个()里面的东西必须使用的是同一个对象才可以
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
}
程序结果正确:this表示谁去调用我,锁定谁,也就是说锁定的是这个对象
qiaodaima
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
huo_chai_gun
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
qiaodaima
huo_chai_gun
qiaodaima
huo_chai_gun
huo_chai_gun
qiaodaima
还有一种解决线程互斥办法就是在方法上加锁:现在有getName1和getName2两个方法
package org.yla;
import java.util.concurrent.TimeUnit;
/**
* 线程的安全问题
*
* @author huo_chai_gun 2014年12月10日下午11:54:45
*/
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init() {
final Outputer outputer = new Outputer();
// 打印第1个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
<span style="color:#FF0000;">outputer.printName1("qiaodaima");</span>
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
// 打印第2个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
<span style="color:#FF0000;">outputer.printName2("huo_chai_gun");</span>
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
// 内部类
class Outputer {
// 打印一个人的名字
public void printName1(String name) {//整个方法都被锁定了 【注意】
int length = name.length();
<span style="color:#FF0000;">synchronized(this){</span>
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
<span style="color:#FF0000;">}</span>
}
public <span style="color:#FF0000;">synchronized </span>void printName2(String name) {//整个方法都被锁定了 【注意】
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
程序运行结果没问题: 【两个方法实现了同步】同事说明 this锁定的是 当前对象
qiaodaima
huo_chai_gun
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
现在继续要求 修改为3个getName方法 要求getName1和getName3实现同步
package org.yla;
import java.util.concurrent.TimeUnit;
/**
* 线程的安全问题
*
* @author huo_chai_gun 2014年12月10日下午11:54:45
*/
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init() {
final Outputer outputer = new Outputer();
// 打印第1个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
<span style="color:#FF0000;">outputer.printName1("qiaodaima");</span>
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
// 打印第2个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
<span style="color:#FF0000;">outputer.printName3("huo_chai_gun");</span>
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
// 内部类
<span style="color:#FF0000;">static </span>class Outputer {
// 打印一个人的名字
public void printName1(String name) {//整个方法都被锁定了 【注意】
int length = name.length();
synchronized(this){
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
public synchronized void printName2(String name) {//整个方法都被锁定了 【注意】
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
public <span style="color:#FF0000;">static synchronized</span> void printName3(String name) {//整个方法都被锁定了 【注意】
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
运行结果:【发现问题】
qiaodaima
huo_chai_gun
huo_cqiahai_gun
odaima
hqiuo_aochdaima
ai_gun
huo_chai_gun
qiaodaima
怎么让getName1和getName3实现同步呢:
修改代码:
package org.yla;
import java.util.concurrent.TimeUnit;
/**
* 线程的安全问题
*
* @author huo_chai_gun 2014年12月10日下午11:54:45
*/
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init() {
final Outputer outputer = new Outputer();
// 打印第1个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
outputer.printName1("qiaodaima");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
// 打印第2个人的名字
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
outputer.printName3("huo_chai_gun");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
// 内部类
static class Outputer {
// 打印一个人的名字
public void printName1(String name) {//整个方法都被锁定了 【注意】
int length = name.length();
synchronized(<span style="color:#FF0000;">Outputer.class</span>){
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
public synchronized void printName2(String name) {//整个方法都被锁定了 【注意】
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
public static synchronized void printName3(String name) {//整个方法都被锁定了 【注意】
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
运行结果:【正确】
qiaodaima
huo_chai_gun
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
huo_chai_gun
qiaodaima
qiaodaima
huo_chai_gun
注意:说明 在静态方法上声明sychronized 锁定的是类.class 不是静态的方法上声明 sychronized 锁定的是当前对象 相当于锁定this