MyException - 我的异常网
当前位置:我的异常网» 编程 » 范例演示ThreadLocal和Synchronized针对多线程处理

范例演示ThreadLocal和Synchronized针对多线程处理

www.MyException.Cn  网友分享于:2014-08-06  浏览:0次
实例演示ThreadLocal和Synchronized针对多线程处理

概述


       在java2后,提供了threadlocal。这样一个新的工具类在处理多线程时提供了另外一种与之前不同的解决方案,而且对于开发者来说更加的简洁。它为每个访问这个变量的线程提供一个线程副本,并将这个副本存入到map中。这样就相当于每个线程都拥有自己独立的变量,在多线程并发操作时就不会造成操作数据的不一致。而在单例模式中,使用到的synchronized。它的机制是控制变量只有单线程进行访问,这样对于变量每次只有一个线程来操作句柄就不会操作数据的不一致。

 

ThreadLocal


        ThreadLocal不是一个本地线程类,是一个提供线程的局部变量的工具类。对于每个访问该变量的线程都拥有初始化变量的副本,位于java.lang下。

 

常见方法



       Get()Set(),通过get(),set()来取得、设置当前线程的副本值。用的最多也就是这两个方法,另外需要构造相应的静态成员变量。


public final static ThreadLocal moneyLocal = new ThreadLocal(); 


       为了在之后所有的线程创建局部变量时不用一一去创建,在类装载时就加载这个静态变量,也是一种饿汉式加载吧!


创建

a.


//第一种声明线程,继承thread
public class ThreadSecond  extends Thread{
	private String ThreadName="";
	public ThreadSecond(){
			this.ThreadName=ThreadName;
		}
	public void run(){
			print();
		}
	public void print(){
		
		String currentThreadName = Thread.currentThread().getName(); 
		System.out.println(currentThreadName+" is running!"); 
		
		}
	public static void main(String[] args){
		
		
		ThreadSecond ts=new ThreadSecond();
		
		ts.start();

	}
} 

通过继承thread,并重写run()来创建threadlocal。

执行结果:

b.

import java.util.*;
import java.lang.*;


class teacher{
	private int salary=0;
	public int returnSalary(){
		return this.salary;
	}
	public void setSalary(int salary){
		this.salary=salary;
	}
}
//第一种创建方式
public	class ThreadTest implements Runnable {
		public void run(){
			setSalary();
			}
		teacher te=new teacher();
		public static void main(String[] args) { 
			
			ThreadTest td=new ThreadTest();
			Thread a=new Thread(td,"a");
			Thread b=new Thread(td,"b");					
	  
			a.start();
			b.start();
		}
	  
	

	public void setSalary(){

			String currentThreadName = Thread.currentThread().getName(); 
			System.out.println(currentThreadName+" is running!"); 
			Random rand =new Random();    
			    
			int salary = rand.nextInt(100); 
			System.out.println("thread "+currentThreadName +" set salary to:"+salary); 
			
			this.te.setSalary(salary); 
			System.out.println("thread "+currentThreadName+" first read salary is:"+this.te.returnSalary()); 
			 
			try { 
			   Thread.sleep(3000); 
			}catch(InterruptedException ex) { 
			       ex.printStackTrace(); 
			}  
			System.out.println("thread "+currentThreadName +" second read salary is:"+this.te.returnSalary()); 
			

	} 
}



执行结果



      如图,这个thread的创建是通过实现了接口来实现的。但在执行完后面一个设置后。线程在第二次访问该变量时出现了一致的情况,a、b线程读取的数据都成了a线程操作后的结果。这就是通常出现的多线程访问时,线程之间对于共享变量的操作出现了问题。

多线程并发操作的两种思路


从以上的出现的问题,最终还是由于在多个线程在访问一个对象的变量时出现的数据修改而导致之后数据访问时造成错误。稍微思考一下可以发现还是可以发现在生活中经常有这样的鲜活的实例的,比如盛饭这样一个问题。盆子里有4两饭,张三需要2两,在第一次盛了1两之后;李四需要3两,李四盛完了3两;这时候张三第二次来访问这个盆子时就没了。怎么解决这个问题呢?张三在盛饭时,保证盆子是张三的,这时候张三拿着这个盆子的句柄;这样无论何时来访问都能够对得上了。或者张三、李四都有这样一个盆子。每个人在自己的盆子里盛饭,谁也不会影响谁。


这样得到两个解决方案:


a.为每个线程单独创建自己的变量

b.保证变量的修改每次只有一个线程在操作


Threadlocal方式    


import java.util.*;
import java.lang.*;

class Money{
	
	private int money;
	
	public int returnMoney(){
		return this.money;
	}
	
	public void setCount(int moneyCount){
		this.money=moneyCount;
	}	

}
public	class ThreadSolve implements Runnable {
		//声明threadLocal静态常量
		public final static ThreadLocal moneyLocal = new ThreadLocal(); 
		
		//重写run方法
		public void run(){
			setMoney();
			}
		
			 
		public static void main(String[] args) { 
			
			ThreadSolve ts=new ThreadSolve();
			
			//声明三个线程
			Thread a=new Thread(ts,"a");
			Thread b=new Thread(ts,"b");		
			Thread c=new Thread(ts,"c");
			  	
			//启动线程
			a.start();
			b.start();
			c.start();
			
		}
	  
	

	public void setMoney(){
		
			//记录线程开始时间
			long startTime=System.currentTimeMillis(); 
			//得到当前线程名称
			String currentThreadName = Thread.currentThread().getName(); 
			System.out.println(currentThreadName+" is running!"); 
			Random rand =new Random();    
			    
			int moneyCount = rand.nextInt(100); 
			System.out.println("thread "+currentThreadName +" set moneyCount to:"+moneyCount); 
			
			//声明线程副本
			Money money=getMoney();
			money.setCount(moneyCount); 
			System.out.println("thread "+currentThreadName+" first read money is:"+money.returnMoney()); 
			 
			try { 
				 //中断当前线程3s
			   Thread.sleep(3000); 
			}catch(InterruptedException ex) { 
			       ex.printStackTrace(); 
			}  
			System.out.println("thread "+currentThreadName +" second read money is:"+money.returnMoney()); 
			//线程结束时间
			long endTime=System.currentTimeMillis(); 
			System.out.println("exuteTime is "+(endTime-startTime));
	}
/*
**得到实例副本
*/
protected Money getMoney() { 
		
		//得到线程副本,任何得到teacher对象都是通过ThreadLocal来get
  	Money money = (Money)moneyLocal.get(); 
  	if(money == null) { 
   		money= new Money(); 
   		moneyLocal.set(money); 
  	} 
  	return money; 
	} 

protected void setMoney(Money money) { 
  	moneyLocal.set(money); 
	}
	  
}
利用threadlocal的两个方法get和set,每次在现场访问时都是通过在ThreadSolve一开始加载时创建的静态变量初始得到的线程副本进行操作的。这样每个线程单独在自己的副本里面操作自己的变量,就不会出现问题。


执行结果



    两个线程执行的大致时间是3s左右。


Synchronized方式


     仔细看看这个类,可以发现出问题的地方就是对变量的修改这里出现问题。将问题的粒度缩小,单个控制对变量修改的线程访问也能够解决问题。至于在哪里控制,可以在访问的时候,或者在调用的时候,都可以。


import java.util.*;
import java.lang.*;



class  teacher{
	private int salary=0;
	public int returnSalary(){
		return this.salary;
	}
	public  void setSalary(int salary){
		this.salary=salary;
	}
}
public	class SyncSolve implements Runnable {
		public void run(){
			setSalary();
			}
		teacher te=new teacher();
		public  static void main(String[] args) { 
			
			SyncSolve td=new SyncSolve();
			Thread a=new Thread(td,"a");
			Thread b=new Thread(td,"b");	
				
	  
			a.start();
			b.start();
		}
	  
	

	public synchronized void setSalary(){
			long startTime=System.currentTimeMillis(); 
			String currentThreadName = Thread.currentThread().getName(); 
			System.out.println(currentThreadName+" is running!"); 
			Random rand =new Random();    
			    
			int salary = rand.nextInt(100); 
			System.out.println("thread "+currentThreadName +" set salary to:"+salary); 
			
			this.te.setSalary(salary); 
			System.out.println("thread "+currentThreadName+" first read salary is:"+this.te.returnSalary()); 
			 
			try { 
			   Thread.sleep(3000); 
			}catch(InterruptedException ex) { 
			       ex.printStackTrace(); 
			}  
			System.out.println("thread "+currentThreadName +" second read salary is:"+this.te.returnSalary()); 
			long endTime=System.currentTimeMillis(); 
			System.out.println("exuteTime is "+(endTime-startTime));
	} 
}




执行结果



      可以明显的看到这个图和上个图不一样的地方,这里每个线程是单独进行的。也就是当第一个线程访问setSalary()时,就对该方法加锁。另外对比两个图,在这种情况下两者的性能是差不太多的,在查阅资料时看到同步会带来巨大的性能开销,这也可以去理解,因为需要在多线程之间共享变量。


总结


      之上,threadlocal 与 Synchronized 都是针对多线程的一种解决方案。不同的是,threadlocal通过创建初始变量副本来为每个线程创建单独的变量;这样单独的线程副本数据之间就不会受影响。这种方式相对Synchronized来说占用内存上有点损耗,而多个线程同时运行的方式相对Synchronized来说性能上也是比较优越。而Synchronized线程操作的都是一个变量,这样利于多线程之间数据的共享。可以说是,各有千秋;没有说哪个好,哪个坏,特定时间特点的环境使用相应的解决方案就可以了。

 

1楼tang_huan_111小时前
在目前咱们所接触到的项目中,有加上多线程的代码了没有?
Re: chenfanglincfl1小时前
回复tang_huan_11n单例模式中就有针对多线程的处理

文章评论

老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
漫画:程序员的工作
漫画:程序员的工作
Java程序员必看电影
Java程序员必看电影
代码女神横空出世
代码女神横空出世
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
一个程序员的时间管理
一个程序员的时间管理
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
我是如何打败拖延症的
我是如何打败拖延症的
旅行,写作,编程
旅行,写作,编程
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
10个调试和排错的小建议
10个调试和排错的小建议
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
程序员的鄙视链
程序员的鄙视链
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
如何成为一名黑客
如何成为一名黑客
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
编程语言是女人
编程语言是女人
我的丈夫是个程序员
我的丈夫是个程序员
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
程序员应该关注的一些事儿
程序员应该关注的一些事儿
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
鲜为人知的编程真相
鲜为人知的编程真相
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员必看的十大电影
程序员必看的十大电影
程序员都该阅读的书
程序员都该阅读的书
每天工作4小时的程序员
每天工作4小时的程序员
程序员和编码员之间的区别
程序员和编码员之间的区别
总结2014中国互联网十大段子
总结2014中国互联网十大段子
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
 程序员的样子
程序员的样子
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有