ThreadLocal的使用,,,實際上相當於維護了一個Map,其中以鍵值對的形式,存儲了某一個數據被多個線程訪問所對應的值。當然這個數據只能有一份,可以是List。。可以是對象。。可以是javabean。。。其Map中每一個Entry保存進去的鍵就是當前線程,值就是這個線程所訪問的那份數據。。。。比方說在服務器端單獨起一個獨立線程來實現與某個客戶端交互,,,其中交互過程裏所有涉及到的公共模塊所訪問的都是一份數據,,所以顧名思義被叫做線程範圍內的數據共享。。。。。想象的到在javaEE中的應用是非常多的。。。。
- package localTest;
- //import java.util.HashMap;
- //import java.util.Map;
- import java.util.Random;
- public class ThreadLocalTest {
- public static int data;
- // public static Map dataMap = new HashMap();
- // public static ThreadLocal dataLocal = new ThreadLocal();
- public static void main(String[] args) {
- for (int i = 0; i < 2; i++) {
- new Thread() {
- public void run() {
- // ThreadLocalTest.data = new Random().nextInt();
- // ThreadLocalTest.dataMap.put(Thread.currentThread().getName(),
- // new Random().nextInt());
- // ThreadLocalTest.dataLocal.set(new Random().nextInt());
- int data = new Random().nextInt();
- MyData.getInstance().setData(data);
- System.out.println(Thread.currentThread().getName()
- + " put data : " + data);
- System.out.println(Thread.currentThread().getName()
- +" A get data : " + new A().get());
- System.out.println(Thread.currentThread().getName()
- +" B get data : " + new B().get());
- }
- }.start();
- }
- }
- }
- class A {
- public int get() {
- // return (Integer)ThreadLocalTest.dataLocal.get();
- return MyData.getInstance().getData();
- }
- }
- class B {
- public int get() {
- // return (Integer)ThreadLocalTest.dataLocal.get();
- return MyData.getInstance().getData();
- }
- }
- class MyData{
- private static ThreadLocal<MyData> myData = new ThreadLocal<MyData>();
- private int data;
- public int getData(){
- return data;
- }
- public void setData(int data){
- this.data = data;
- }
- private MyData(){}
- public static MyData getInstance(){
- MyData data = myData.get();
- if(data==null){
- data = new MyData();
- myData.set(data);
- }
- return data;
- }
- }
接着是線程併發庫中的java.util.concurrent.atomic
包中提供了對一些數據類型的擴展,使得對那些數據類型的操作封裝成爲了方法,而那些方法都是線程安全的,atomic解釋是原子的,,支持在單個變量上解除鎖的線程安全編程。。。。其中包括Integer,boolean以及IntegerArray和引用類型。。
接下來就是線程池了,,於jdbc數據庫連接池同理,,大大的提高了程序運行的效率。。因爲創建一個新的線程還是很耗資源的。。
我們也只需要用到一個類,,java.util.concurrent包的老大,,,Executors類,,提供一系列靜態方法,用於創建各種線程池,,其中包含固定大小線程,和自動大小線程池(通過任務數量來分配線程數量),以及單一線程池(始終保持有一個線程在池子裏)。。。還有可調度的線程池,相當於定時器。。其中定時器任務代碼內還可嵌套另外的定時器。。。通過調用線程池的shutdown方法在任務執行完畢後殺死池內線程。shutdownNow直接殺死。。interrupt!ALL!!!
- package concurrent;
- import java.util.Random;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- public class ThreadPoolTest {
- public static void main(String[] args) {
- ExecutorService pool = Executors.newCachedThreadPool();//Executors.newFixedThreadPool(3);
- for (int i = 0; i < 10; i++) {
- final int taskId = i;
- pool.execute(new Runnable() {
- public void run() {
- for (int j = 0; j < 10; j++) {
- try {
- Thread.sleep(new Random().nextInt(1000));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName()
- + " execute task " + taskId +
- " loop for " + j);
- }
- }
- });
- }
- pool.shutdown();
- }
- }
- package concurrent;
- import java.util.Calendar;
- import java.util.concurrent.Executors;
- import java.util.concurrent.ScheduledExecutorService;
- import java.util.concurrent.TimeUnit;
- public class TimerTaskTest {
- private static int count;
- public static void main(String[] args) {
- System.out.println(Calendar.getInstance().get(Calendar.SECOND));
- final ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
- class MyTask implements Runnable{
- public void run(){
- count++;
- System.out.println(Calendar.getInstance().get(Calendar.SECOND));
- System.out.println("bombing!");
- if(count%2==1)
- timer.schedule(new MyTask(), 4, TimeUnit.SECONDS);
- else
- timer.schedule(new MyTask(), 3, TimeUnit.SECONDS);
- }
- }
- timer.schedule(new MyTask(), 2, TimeUnit.SECONDS);
- }
- }
Callable讓線程能夠擁有返回值,類似於Runnable接口,,需要實現call方法,,Future類接受一個Callable的返回值。。。通過get方法獲取返回值,當沒有數據時,它將會等待,什麼時候有數據什麼時候獲取,通過get方法的重載方法可以設置等待時間。。多個Callable利用CompletionService對象來接收返回值,通過submit來接受提交的任務!!!接受返回值時誰先有數據就提取誰的數據!通過take方法放回一個Future對象再調用get方法獲取數據。
- package concurrent;
- import java.util.concurrent.Callable;
- import java.util.concurrent.CompletionService;
- import java.util.concurrent.Executor;
- import java.util.concurrent.ExecutorCompletionService;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.Future;
- import java.util.concurrent.TimeUnit;
- public class CallableTest {
- public static void main(String[] args) throws Exception {
- ExecutorService pool = Executors.newSingleThreadExecutor();
- Future<String> future = pool.submit(new Callable<String>() {
- @Override
- public String call() throws Exception {
- // TODO Auto-generated method stub
- Thread.sleep(3000);
- return "Hello world!";
- }
- });
- try {
- System.out.println(future.get(4,TimeUnit.SECONDS));
- } catch (Exception e) {
- e.printStackTrace();
- }
- pool.shutdown();
- submit();
- }
- public static void submit() throws Exception{
- Executor executor = Executors.newCachedThreadPool();
- CompletionService<Integer> service = new ExecutorCompletionService<Integer>(executor);
- for(int i=0;i<10;i++){
- final int sep = i;
- service.submit(new Callable<Integer>() {
- public Integer call() throws Exception {
- Thread.sleep((int)(Math.random()*1000));
- return sep;
- }
- });
- }
- for(int i=0;i<10;i++){
- System.out.println(service.take().get());
- }
- }
- }
lock,,,普通鎖,,以及讀寫鎖,,可以實現讀與寫之間互斥,讀於讀之間可以並行,寫於寫之間也互斥!!
面試題一道,,通過讀寫鎖編寫一個緩存類得實際應用
- package concurrent;
- import java.util.*;
- import java.util.concurrent.locks.ReadWriteLock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
- public class CacheClass {
- public static void main(String[] args) {
- //test();
- }
- private ReadWriteLock rwl = new ReentrantReadWriteLock();
- private Map<String,Object> cacheData = new HashMap<String,Object>();
- public Object get(String key){
- rwl.readLock().lock();
- Object obj = null;
- try {
- obj = cacheData.get(key);
- if(obj==null){
- rwl.readLock().unlock();
- rwl.writeLock().lock();
- obj = "Shawn";//readDatabase();
- cacheData.put(key, obj);
- rwl.writeLock().unlock();
- rwl.readLock().lock();
- }
- } finally {
- rwl.readLock().unlock();
- }
- return obj;
- }
- }
以及通過面向對象設計的一另一道面試題,,子線程執行5次,主線程執行10次,這樣循環總共10次。。。通過synchronized關鍵字修飾子線程和主線程需要執行的方法封裝到一個類當中,通過一個boolean變量來標識子線程和主線程的執行次序。。。然後各自循環10次。。。同樣實現兩個子線程和一個主線程來交替執行,這個時候就需要用到顯示鎖,通過多個條件變量(condition)來實現,,線程1獲得鎖,執行完釋放並通知條件變量2,線程2獲得鎖,執行完釋放並通知條件變量3,以此類推,,4個或5個線程交替執行也同理!!!
- package concurrent;
- public class InterViewSubject {
- public static void main(String[] args) {
- init();
- }
- private static void init() {
- final MyData data = new MyData();
- new Thread() {
- public void run() {
- for (int i = 0; i < 10; i++) {
- data.sub();
- }
- }
- }.start();
- for (int i = 0; i < 10; i++) {
- data.main();
- }
- }
- }
- class MyData {
- private boolean isMain;
- public synchronized void sub() {
- if (!isMain) {
- for (int i = 0; i < 5; i++) {
- System.out.println("sub Thread loop for : " + i);
- }
- } else
- try {
- this.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- isMain = true;
- this.notify();
- }
- public synchronized void main() {
- if (isMain) {
- for (int i = 0; i < 10; i++) {
- System.out.println("main Thread loop for : " + i);
- }
- } else
- try {
- this.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- isMain = false;
- this.notify();
- }
- }
歡迎大家評論,,大家能夠討論討論咱們所學的知識點有些什麼實際應用場景!!!這也是我們最終學習新知識的最終目的!!!