1. 程式人生 > >執行緒(五)ThreadLocal解決執行緒範圍內資料的共享

執行緒(五)ThreadLocal解決執行緒範圍內資料的共享

1、該方法缺點是隻能放入一個數據

package 多執行緒;

import java.util.Random;

//ThreadLocal來完成執行緒範圍內資料的共享
public class pthread {
	
	private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
	
	public static void main(String[] args) {
		for (int i = 0; i < 2; i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() + " has put a data: " + data);
					threadLocal.set(data); //將資料直接扔進去
					new TestA().getData();
					new TestB().getData();
				}
			}).start();
		}
	}
	
	static class TestA {
		public void getData() {
			System.out.println(" A get data from " + Thread.currentThread().getName() + ":" + threadLocal.get());
		}
		
	}
	
	static class TestB {
		public void getData() {
			System.out.println(" B get data from " + Thread.currentThread().getName() + ":" + threadLocal.get());
		}
		
	}
	

}

2、放入多個數據,如下:

public class ThreadScopeShareData {
//不需要在外面定義threadLocal了,放到User類中了
//  private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();

    public static void main(String[] args) {
        for(int i = 0; i < 2; i ++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " has put a data: " + data);

                    //這裡直接用User去呼叫getThreadLocal這個靜態方法獲取本執行緒範圍內的一個User物件
                    //這裡就優雅多了,我完全不用關心如何去拿該執行緒中的物件,如何把物件放到threadLocal中
                    //我只要拿就行,而且拿出來的肯定就是當前執行緒中的物件,原因看下面User類中的設計
                    User.getThreadInstance().setName("name" + data);
                    User.getThreadInstance().setAge(data);

                    new TestA().getData();
                    new TestB().getData();
                }
            }).start();
        }
    }

    static class TestA {
        public void getData() {
            //還是呼叫這個靜態方法拿,因為剛剛已經拿過一次了,threadLocal中已經有了
            User user = User.getThreadInstance();
            System.out.println("A get data from " + Thread.currentThread().getName() + ": " 
                    + user.getName() + "," + user.getAge());
        }
    }

    static class TestB {
        public void getData() {

            User user = User.getThreadInstance();
            System.out.println("A get data from " + Thread.currentThread().getName() + ": " 
                    + user.getName() + "," + user.getAge());
        }
    }

}

class User {

    private User() {}

    private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();

    //注意,這不是單例,每個執行緒都可以new,所以不用synchronized,
    //但是每個threadLocal中是單例的,因為有了的話就不會再new了
    public static /*synchronized*/ User getThreadInstance() {
        User instance = threadLocal.get(); //先從當前threadLocal中拿
        if(instance == null) {
            instance = new User();
            threadLocal.set(instance);//如果沒有就新new一個放到threadLocal中
        }
        return instance; //向外返回該User
    }

    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

這個方法可以一次放入多個數據,並且通過類似單例的方式,不需要在將new出來的物件存入threadLocal中。

以上方法是看武哥的文章。

3、在實際專案中,常常會遇到執行緒內資料共享的問題,我就遇到類似問題:

//privat static int nTimes = entry.getKey();


.........


ScheduledFuture<?> scheduledFuture = pool.scheduleWithFixedDelay(new Runnable()
 {
      int nTimes = 0;
       @Override
       public void run() {
              nTimes++;
              Map jsonMap = JacksonUtil.json2Bean(message, Map.class);
              int ChargeLastTime = Integer.parseInt((String) jsonMap.get("EndTime")) - Integer.parseInt((String) jsonMap.get("StartTime")); //充電時長
              chargeOrderInfoReqVo.setUserName((String) jsonMap.get("UserName"));
              chargeOrderInfoReqVo.setStationID((String) jsonMap.get("StationID"));  //運營商自定義唯一編碼?
              chargeOrderInfoReqVo.setEquipmentID((String) jsonMap.get("EquipmentID")); //運營商裝置唯一編碼?
              chargeOrderInfoReqVo.setConnectorPower(Float.parseFloat((String) jsonMap.get("ConnectorPower")) / 1000);
               chargeOrderInfoReqVo.setChargeLast(ChargeLastTime);
               chargeOrderInfoReqVo.setMeterValueStart(Float.parseFloat((String) jsonMap.get("MeterValueStart")) / 100);
               chargeOrderInfoReqVo.setMeterValueEnd(Float.parseFloat((String) jsonMap.get("MeterValueEnd")) / 100);
               LOG.info("------延時------");
               try {
                    Thread.sleep(150000 * nTimes);
               }catch (InterruptedException e){
                        e.printStackTrace();
               }
         }
 }, 10, nTimes * 10, TimeUnit.SECONDS);

我的想法是想要執行緒每執行一次,就延長一次,同時延長間隔變長,貌似如上好像可以。但在實際過程中,nTimes是一個定值;

意思就是執行緒類的nTimes * 10並沒有變化,這個問題還需要繼續研究。。。