精华内容
下载资源
问答
  • 1. 为什么要使用线程局部变量你? 如果创建的对象实现了Runnable接口的类的实例,用它作为传入参数,并创建多个线程对象并启动这些线程,那么所有的线程将共享相同的属性。如果在一个线程改变一个属性,所有线程...

    1. 为什么要使用线程局部变量你?

    如果创建的对象实现了Runnable接口的类的实例,用它作为传入参数,并创建多个线程对象并启动这些线程,那么所有的线程将共享相同的属性。如果在一个线程中改变一个属性,所有线程都会被这个改变影响。这样,不得不处理同步的问题。

    要想把线程私有数据(如一个用户ID)和线程关联起来,可以使用线程局部变量 java.lang.ThreadLocal类

    2. 什么是线程局部变量?

    每个ThreadLocal的实例代表了一个线程局部变量,它为每一条访问线程提供了单独的存储槽(storage slot)。可以把它想象成具有多个槽的变量,然后每条线程可以在同一个变量中存储不同的值。并且,每条线程都只能 看到自己的值,而不会意识到其他线程在这个变量中也有自己的值。

    声明方式:ThreadLocal<T>

    3.构造函数和方法:

    (1)ThreadLocal(): 创建了一个新的线程局部变量。

    (2)T get(): 返回调用线程的存储槽中的值。如果当这个线程调用此方法时,值不存在,那么get()会调用 initialValue()方法。

    (3)T initialValue(): 创建调用线程的存储槽并存入一个初始(默认)值。默认的初始值是null。

    (4)void remove(): 清空调用线程的存储槽。在没有set()方法介入的情况下,如果紧随此方法之后调用get()方法,那么get()方法就会直接调用initialValue()。

    (5)void set(T value):设置调用线程的存储槽上的值。

    //使用线程局部变量关联不同用户的ID
    
    public class ThreadLocalDemo {
    
    	private static volatile ThreadLocal<String> userID = new ThreadLocal<>();
    	
    	public static void main(String[] args)
    	{
    		Runnable r = new Runnable()
    				{
    			         @Override
    			         public void run()
    			         {
    			        	 String name = Thread.currentThread().getName();
    			        	 if(name.equals("A"))
    			        		 userID.set("foxtrot");
    			        	 else
    			        		 userID.set("charlie");
    			        		 
    			        	 System.out.println(name + " " + userID.get());
    			         }
    				};
    				
    		Thread t1 = new Thread(r, "A");
    		Thread t2 = new Thread(r, "B");
    		t1.start();
    		t2.start();
    	}
    }
    

    运行结果:

    A foxtrot
    B charlie

    4. 存储在线程局部变量中的值都是相关的。当一个新的线程被创建出来,它会获得一个新的包含initialValue()值的存储槽。

    当想要把值从父类传给子线程时,可以使用InheritableThreadLocal。

    InheritableThreadLocal是ThreadLocal的子类,除了InheritableThreadLocal()构造方法,还有:

    T childValue(T parentValue):在子线程被创建出来的时候,用父线程的值(即参数)计算出子线程的初始值。

    
    //将一个对象从父线程传到子线程
    public class InheritableThreadLocalDemo {
    
    	private static volatile InheritableThreadLocal<Integer> intVal = new InheritableThreadLocal<Integer>();
    	
    	public static void main(String[] args)
    	{
    		Runnable rParent = new Runnable()
    				{
    			         @Override
    			         public void run()
    			         {
    			        	 intVal.set(10);  //父线程在intVal中存储一个值为10的Integer
    			        	 Runnable rChild = new Runnable()
    			        			 {
    			        		          @Override
    			        		          public void run()
    			        		          {
    			        		        	  String name = Thread.currentThread().getName();
    				        		           System.out.printf("%s %d%n",name,intVal.get());
    			        		          }			        		  	 
    			        			 };
    			        	Thread thdChild = new Thread(rChild,"Child");
    			        	thdChild.start();
    			        	
    			         }
    				};
    				
    				new Thread(rParent).start();
    	}
    }
    

    主线程创建了一条父线程,这条线程在intVal中存储了一个值为10的java.lang.Integer对象。父线程之后创建了一条子线程,这条线程访问intVal并取得父线程中的Integer对象。 

     

    展开全文
  • 共享数据是并发程序最核心的问题之一,对于继承Thread类或者实现Runnable接口的对象来说尤其重要。如果创建的对象实现了Runnable接口的类的实例,用它作为传入参数...JAVA提供了一个比较好的机制,即线程局部变量(T...

    共享数据是并发程序最核心的问题之一,对于继承Thread类或者实现Runnable接口的对象来说尤其重要。

    如果创建的对象实现了Runnable接口的类的实例,用它作为传入参数,并创建多个线程对象并启动这些线程,那么所有的线程将共享相同的属性。如果在一个线程中改变一个属性,所有线程都会被这个改变影响。

    在某种情况下,这个对象的属性不需要被所有线程共享。JAVA提供了一个比较好的机制,即线程局部变量(Thread-Local Variable).

    我们写一个简单的DEMO,一是具有刚才提到的问题,另一个使用线程局部变量机制解决这个问题。

    1.创建一个类为UnsafeTask的类,它实现了Runnable接口。并且声明一个java.util.Date属性。

    public class UnsafeTask implements Runnable{

    private Date startDate;  }

    2.实现run()方法。这个方法初始化startDate属性,并且将值打印到控制台。让线程休眠一个随机时间,然后再次将值打印到控制台。

    public void run(){

    startDate=new Date();

    System.out.printf("Starting Thread:%s:%s \n",Thread.currentThread().getId(),startDate);

    try{

    TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10));

    }catch(InterruptedException e){

    e.printStackTrace();

    }

    System.out.printf("Thread Finished:%s :%s\n",Thread.currentThread().getId(),startDate);

    }

    3.创建一个Main主类,并包含了一个main()方法。这个方法将创建一个UnsafeTask类对象,用它作为传入参数创建10个线程对象并启动10个线程,每个线程启动时间间隔为2秒。

    public class Main {

    public static void main(String[] args){

    UnsafeTask task=new UnsafeTask();

    for(int i=0;i<10;i++){

    Thread thread=new Thread(task);

    thread.start();

    try{

    TimeUnit.SECONDS.sleep(2);

    }catch(InterruptedException e){

    e.printStackTrace();

    }

    }

    }

    }

    4.运行结果如图

    pcXHYZkMAAAAABJRU5ErkJggg==

    每个线程有一个不同的开始时间。他们结束时,三个线程就有相同的startDate属性值。

    9PaxeVniUznWi6D1ps36dGy2K52Nfj++eq8BEskph+BNjhvsPVhgEQRAEQRA5UGEQBEEQBFEPKgyCIAiCIOpBhUEQBEEQRD2oMAiCIAiCqAcVBkEQBEEQ9aDCIAiCIAiiHlQYBEEQBEHUgwqDIAiCIIh6UGEQBEEQBFEPKgyCIAiCIOrxP7P07YiGnM8IAAAAAElFTkSuQmCC

    5.接下来我们使用线程局部变量机制来解决上述问题。

    6.创建一个SafeTask类,用以实现Runnable接口。

    public class SafeTask implements Runnable{}

    7.声明一个ThreadLocal对象。这个对象是在initialValue()方法中隐式实现,返回当前日期。

    private static ThreadLocal startDate=new ThreadLocal(){

    protected Date initalValue(){

    return new Date();

    }

    };

    8.实现run()方法。跟上面方法实现一样功能,但是startDate属性方式改变了。

    public void run(){

    System.out.printf("Starting Thread: %s : %s\n",Thread.currentThread().getId(),startDate.get());

    try {

    TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10));

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    System.out.printf("Thread Finished: %s : %s\n",Thread.currentThread().getId(),startDate.get());

    }

    9.Main主类同上,只是创建并作为参数传入的Runnable类对象不同而已。

    public class Main {

    public static void main(String[] args){

    SafeTask task=new SafeTask();

    for(int i=0;i<3;i++){

    Thread thread=new Thread(task);

    try{

    TimeUnit.SECONDS.sleep(2);

    }catch(InterruptedException e){

    e.printStackTrace();

    }

    thread.start();

    }

    }

    }

    10.运行结果。

    +EEEIIIZ8BpWQOsWvwhBBCCCFfAaVkDoKUpIwkhBBCyLdDKUkIIYQQQgb5BypedPedsGrgAAAAAElFTkSuQmCC

    bKn20okFmdwAAAABJRU5ErkJggg==

    原文:http://www.cnblogs.com/wanming88/p/4151391.html

    展开全文
  • 并发的线程能不能看到变量的最新值,这就是并发中变量可见性问题 方式一 synchronized 1.进入同步块,先清空工作内存的共享变量,从 主内存重新加载 2.解锁前必须把修改的共享变量同步回主内存 方式二 ...

    并发的线程能不能看到变量的最新值,这就是并发中的变量可见性问题

    方式一 synchronized

    1.进入同步块,先清空工作内存中的共享变量,从
    主内存中重新加载
    2.解锁前必须把修改的共享变量同步回主内存

    方式二 volatile

    volatile可用于限制局部代码指令重排序

    1.使用volatile变量时,必须重新从主内存加载,并且read,load是连续的
    2.修改volatile变量后,必须立马同步回主内存,并且store write是连续的

    使用场景:
    1.只可修饰成员变量
    2.多线程并发下,才需要使用它

    volatile典型的应用场景
    1.只有一个修改者,多个使用者,要求保证可见性的场景
    1.状态

    方式三 final

    静态属性放在方法区 堆

    线程是在栈中

    共享变量必须存放在主内存中

    线程有自己的工作内存

    JAVA内存模型 同步交换协议 规定了8种原子操作
    1.lock
    2.unlock
    3.read
    4.load 载入
    5.use 使用
    6.assign 赋值
    7.store 存储
    8.write 写入

    展开全文
  • 共享数据是并发程序最核心的问题之一,对于继承Thread类或者实现Runnable接口的对象来说尤其重要。 如果创建的对象实现了Runnable接口的类的实例,用它作为传入参数,并创建多个线程对象并启动这些线程,那么所有的...

          共享数据是并发程序最核心的问题之一,对于继承Thread类或者实现Runnable接口的对象来说尤其重要。

          如果创建的对象实现了Runnable接口的类的实例,用它作为传入参数,并创建多个线程对象并启动这些线程,那么所有的线程将共享相同的属性。如果在一个线程中改变一个属性,所有线程都会被这个改变影响。

         在某种情况下,这个对象的属性不需要被所有线程共享。JAVA提供了一个比较好的机制,即线程局部变量(Thread-Local Variable).

         我们写一个简单的DEMO,一是具有刚才提到的问题,另一个使用线程局部变量机制解决这个问题。

         1.创建一个类为UnsafeTask的类,它实现了Runnable接口。并且声明一个java.util.Date属性。

           public class UnsafeTask implements Runnable{
           private Date startDate;  }

        2.实现run()方法。这个方法初始化startDate属性,并且将值打印到控制台。让线程休眠一个随机时间,然后再次将值打印到控制台。

           public void run(){
              startDate=new Date();
              System.out.printf("Starting Thread:%s:%s \n",Thread.currentThread().getId(),startDate);
              try{
                  TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10));
              }catch(InterruptedException e){
                  e.printStackTrace();
              }
              System.out.printf("Thread Finished:%s :%s\n",Thread.currentThread().getId(),startDate);
           }

         3.创建一个Main主类,并包含了一个main()方法。这个方法将创建一个UnsafeTask类对象,用它作为传入参数创建10个线程对象并启动10个线程,每个线程启动时间间隔为2秒。

        public class Main {
         public static void main(String[] args){
             UnsafeTask task=new UnsafeTask();
             for(int i=0;i<10;i++){
                 Thread thread=new Thread(task);
                 thread.start();
                 try{
                    TimeUnit.SECONDS.sleep(2);
                 }catch(InterruptedException e){
                     e.printStackTrace();
                    
                 }
             }
         }
    }

       4.运行结果如图

         

          每个线程有一个不同的开始时间。他们结束时,三个线程就有相同的startDate属性值。

        

          5.接下来我们使用线程局部变量机制来解决上述问题。

          6.创建一个SafeTask类,用以实现Runnable接口。

            public class SafeTask implements Runnable{}

         7.声明一个ThreadLocal<Date>对象。这个对象是在initialValue()方法中隐式实现,返回当前日期。

            private static ThreadLocal<Date> startDate=new ThreadLocal<Date>(){
               protected Date initalValue(){
                   return new Date();
               }
          };

          8.实现run()方法。跟上面方法实现一样功能,但是startDate属性方式改变了。

           public void run(){
            System.out.printf("Starting Thread: %s : %s\n",Thread.currentThread().getId(),startDate.get());
              try {
                  TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10));
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              
              System.out.printf("Thread Finished: %s : %s\n",Thread.currentThread().getId(),startDate.get());
          }

          9.Main主类同上,只是创建并作为参数传入的Runnable类对象不同而已。

          public class Main {
         public static void main(String[] args){
             SafeTask task=new SafeTask();
             for(int i=0;i<3;i++){
                 Thread thread=new Thread(task);
            
                 try{
                    TimeUnit.SECONDS.sleep(2);
                 }catch(InterruptedException e){
                     e.printStackTrace();
                    
                 }
                 thread.start();
             }
         }
    }

         10.运行结果。

       

        

      

          

    转载于:https://www.cnblogs.com/wanming88/p/4151391.html

    展开全文
  • java猿在面试,经常会被问到1个问题: java实现同步有哪几种方式? 大家一般都会回答使用synchronized, 那么还有其他方式吗? 答案是肯定的, 另外一种方式也就是本文要说的ThreadLocal。 ThreadLoc...
  • java线程安全问题之静态变量、实例变量、局部变量 转载 2012年12月14日 15:08:06 33721  java多线程编程,存在很多线程安全问题,至于什么是线程安全呢,给出一个通俗易懂的概念还是蛮难的...
  • 1.什么是ThreadLocal  ThreadLocal顾名思义是线程局部变量。...线程局部变量不存在多个线程同时对同一个变量的操作,所以不会有线程安全问题。 2.ThreadLocal变量的使用 public class ThreadLocalDemo {  priva...
  • 转载   java多线程编程,存在很多线程安全问题,至于什么是线程安全呢,给出... 此处不赘述了,首先给出静态变量、实例变量、局部变量在多线程环境下的线程安全问题结论,然后用示例验证,请大家擦亮眼睛,有...
  • java多线程编程,存在很多线程安全问题... 此处不赘述了,首先给出静态变量、实例变量、局部变量在多线程环境下的线程安全问题结论,然后用示例验证,请大家擦亮眼睛,有错必究,否则误人子弟!静态变量:线程非安...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 691
精华内容 276
关键字:

并发问题中局部变量