多线程03_张孝祥-传统线程互斥技术

本本章位作者原创,转载请注明出处,欢迎大家一起交流,欢迎高手赐教,我的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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值