Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android面試之J2SE基礎

Android面試之J2SE基礎

編輯:關於Android編程

21. 實現多線程的兩種方法

實現Runnable接口:實際工作中,幾乎所有的多線程應用都用實現Runnable這種方式。
Runnable適合多個相同程序代碼的線程去處理統一資源的情況。把虛擬CPU通程序的代碼、數據有效的分離,較好的體現了面向對象的設計思想。 避免由於Java的單繼承特性帶來的局限性。也就是如果新建的類要繼承其他類的話,因為JAVA中不支持多繼承,就只能實現java.lang.Runnable接口。 有利用程序的健壯性,代碼能夠被多個線程共享,代碼與數據是獨立的。 繼承Thread類:
不能再繼承其他類 編寫簡單,可直接操縱線程,無需使用Thread.currentThread().

22. 線程同步的方法

synchronized關鍵字和Lock:當使用線程來同時運行多個任務時,可以通過使用鎖(互斥)來同步兩個任務的行為,從而使得一個任務不會干涉另一個任務的資源。也就是說,如果兩個任務在交替著步入某項共享資源,你可以使用互斥來使得任何時刻只有一個任務可以訪問這項資源。

synchronized:在資源競爭不是很激烈的情況下,Synchronized的性能要優於ReetrantLock,但是在資源競爭很激烈的情況下,Synchronized的性能會下降幾十倍,但是ReetrantLock的性能能維持常態;在資源競爭不是很激烈的情況下,偶爾會有同步的情形下,synchronized是很合適的。原因在於,編譯程序通常會盡可能的進行優化synchronize,另外可讀性非常好,不管用沒用過5.0多線程包的程序員都能理解。 ReentrantLock:ReentrantLock 擁有Synchronized相同的並發性和內存語義,此外還多了 鎖投票,定時鎖等候和中斷鎖等候。ReentrantLock提供了多樣化的同步,比如有時間限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在資源競爭不激烈的情形下,性能稍微比synchronized差點點。但是當同步非常激烈的時候,synchronized的性能一下子能下降好幾十倍。而ReentrantLock確還能維持常態。
ReentrantLock獲取鎖定與三種方式:
lock(), 如果獲取了鎖立即返回,如果別的線程持有鎖,當前線程則一直處於休眠狀態,直到獲取鎖。 tryLock(), 如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false。 tryLock(long timeout,TimeUnit unit), 如果獲取了鎖定立即返回true,如果別的線程正持有鎖,會等待參數給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false。 lockInterruptibly:如果獲取了鎖定立即返回,如果沒有獲取鎖定,當前線程處於休眠狀態,直到或者鎖定,或者當前線程被別的線程中斷。 Atomic:和上面的類似,不激烈情況下,性能比synchronized略遜,而激烈的時候,也能維持常態。激烈的時候,Atomic的性能會優於ReentrantLock一倍左右。但是其有一個缺點,就是只能同步一個值,一段代碼中只能出現一個Atomic的變量,多於一個同步無效。因為他不能在多個Atomic之間同步。 線程A和B都要獲取對象O的鎖定,假設A獲取了對象O鎖,B將等待A釋放對O的鎖定,如果使用synchronized ,如果A不釋放,B將一直等下去,不能被中斷。如果使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長的時間以後,中斷等待,而干別的事情。 synchronized是在JVM層面上實現的,不但可以通過一些監控工具監控synchronized的鎖定,而且在代碼執行時出現異常,JVM會自動釋放鎖定,但是使用Lock則不行,lock是通過代碼實現的,要保證鎖定一定會被釋放,就必須將unLock()放到finally{}中

Object的方法wait()和notify()或者Condition的await()和signal()方法:任務彼此之間協作一起去解決某個問題,在解決這個問題中,由於某些部分必須在其他部分被解決之前解決。

wait():使一個線程處於等待狀態,並且釋放所持有的對象的lock。會在等待外部世界產生變化時將任務掛起,並且只有在notify或notifyAll發生時,這個任務才會被喚醒病區檢查所產生的變化。因此,wait()提供了一種在任務之間對活動同步的方式。 sleep():使一個正在運行的線程處於睡眠狀態,是Thread的一個靜態方法,調用此方法需要捕捉InterruptedException異常,沒有釋放鎖。 notify():在眾多等待同一個鎖的任務中只有一個會被喚醒。注意的是在調用此方法時,並不能確切的喚醒某一個等待的線程,而是有JVM確定喚醒哪個線程,而不是按優先級。 notifyAll():將喚醒所有正在等待的任務。當notifyAll()因某個特定所而被調用時,只有等待這個鎖的任務才會被喚醒,注意不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。

23. 鎖的等級

方法鎖:對象鎖(也叫方法鎖)。同步靜態方法/靜態變量互斥體,由於一個class不論被實例化多少次,其中的靜態方法和靜態變量在內存中都只由一份。所以,一旦一個靜態的方法被申明為synchronized。此類所有的實例化對象在調用此方法,共用同一把鎖,我們稱之為類鎖。一旦一個靜態變量被作為synchronized block的mutex。進入此同步區域時,都要先獲得此靜態變量的對象鎖。 對象鎖:是針對一個對象的,它只在該對象的某個內存位置聲明一個標志位標識該對象是否擁有鎖,所以它只會鎖住當前的對象。一般一個對象鎖是對一個非靜態成員變量進行syncronized修飾,或者對一個非靜態方法進行syncronized修飾。對於對象鎖,不同對象訪問同一個被syncronized修飾的方法的時候不會阻塞住。 類鎖:是鎖住整個類的,當有多個線程來聲明這個類的對象的時候將會被阻塞,直到擁有這個類鎖的對象被銷毀或者主動釋放了類鎖。這個時候在被阻塞住的線程被挑選出一個占有該類鎖,聲明該類的對象。其他線程繼續被阻塞住。 對象鎖是用來控制實例方法之間的同步,類鎖是用來控制靜態方法(或靜態變量互斥體)之間的同步。所謂的類鎖,不過是Class對象的鎖而已。獲取類的Class對象有好幾種,最簡單的就是MyClass.class的方式。

24. 寫出生產者消費者模式

wait()和notify()實現:
package com.xqq.生產者與消費者之wait和notify實現;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 用於存放生產數據的緩沖區隊列
 * @author xqq
 */
public class FlowQueue {
    private Queue queue = new LinkedList();
    private int maxSize;
    public FlowQueue(int maxSize) {
        this.maxSize = maxSize;
    }
    public synchronized void put(T v) throws InterruptedException{
        while(queue.size() >= maxSize){
            wait();
        }
        queue.offer(v);
        notifyAll();
    }

    public synchronized T get() throws InterruptedException{
        while(queue.isEmpty()){
            wait();
        }
        T returnVal = queue.poll();
        notifyAll();
        return returnVal;
    }
}

package com.xqq.生產者與消費者之wait和notify實現;

import java.util.concurrent.TimeUnit;

public class Consumer implements Runnable{
    private int delay;
    private FlowQueue input;

    public Consumer(int delay, FlowQueue input) {
        this.delay = delay;
        this.input = input;
    }
    public void run() {
        for(;;){
            try {
                System.out.println("Consumer " + input.get());
                TimeUnit.MILLISECONDS.sleep(delay);
            } catch (Exception e) {
                return;
            }
        }
    }
}

package com.xqq.生產者與消費者之wait和notify實現;

import java.util.concurrent.TimeUnit;

public class Producer implements Runnable{
    private int delay;
    private FlowQueue output;

    public Producer(int delay, FlowQueue output) {
        this.delay = delay;
        this.output = output;
    }
    public void run() {
        for(;;){
            try {
                Item product = new Item();
                System.out.println("Product " + product);
                output.put(product);
                TimeUnit.MILLISECONDS.sleep(delay);
            } catch (Exception e) {
                return;
            }
        }
    }
}

package com.xqq.生產者與消費者之wait和notify實現;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ProducerConsumer {
    public static void main(String[] args) throws InterruptedException {
        int producerSleep = 1;
        int consumerSleep = 200;
        FlowQueue fq = new FlowQueue(10);
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new Producer(producerSleep, fq));
        exec.execute(new Consumer(consumerSleep, fq));
        TimeUnit.SECONDS.sleep(2);
        exec.shutdownNow();
    }
}

運行結果:
Product Item 0
Consumer Item 0
Product Item 1
Product Item 2
Product Item 3
Product Item 4
Product Item 5
Product Item 6
Product Item 7
Product Item 8
Product Item 9
Product Item 10
Product Item 11
Consumer Item 1
Product Item 12
Consumer Item 2
Product Item 13
Consumer Item 3
Product Item 14
Consumer Item 4
Product Item 15
Consumer Item 5
Product Item 16
Consumer Item 6
Product Item 17
Consumer Item 7
Product Item 18
Consumer Item 8
Product Item 19
Consumer Item 9
Product Item 20
BlockingQueue實現:
package com.xqq.生產者與消費者BlockingQueue實現;

import java.util.concurrent.BlockingQueue;

public class Consumer implements Runnable {
    private BlockingQueue sharedQueue;
    public Consumer(BlockingQueue sharedQueue) {
        this.sharedQueue = sharedQueue;
    }
    public void run() {
        for (;;) {
            try {
                System.out.println("Consumer " + sharedQueue.take());
            } catch (Exception e) {
                return;
            }
        }
    }
}

package com.xqq.生產者與消費者BlockingQueue實現;

import java.util.concurrent.BlockingQueue;

public class Producer implements Runnable{
    private BlockingQueue sharedQueue;

    public Producer(BlockingQueue sharedQueue) {
        this.sharedQueue = sharedQueue;
    }
    public void run() {
        for(int i = 0; i<10; i++){
            try {
                System.out.println("Produce " + i);
                sharedQueue.put(i);
            } catch (Exception e) {
                return ;
            }
        }
    }
}

package com.xqq.生產者與消費者BlockingQueue實現;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class ProducerConsumerPattern {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        BlockingQueue sharedQueue = new LinkedBlockingQueue();
        exec.execute(new Producer(sharedQueue));
        exec.execute(new Consumer(sharedQueue));
        TimeUnit.SECONDS.sleep(3);
        exec.shutdownNow();
    }
}

運行結果:
Produce 0
Produce 1
Produce 2
Produce 3
Produce 4
Produce 5
Produce 6
Produce 7
Produce 8
Produce 9
Consumer 0
Consumer 1
Consumer 2
Consumer 3
Consumer 4
Consumer 5
Consumer 6
Consumer 7
Consumer 8
Consumer 9

25. ThreadPool用法與優勢

優勢:
降低資源消耗:通過重復利用以創建的線程降低創建線程和銷毀線程的消耗。 提高響應速度:當任務到達時,任務可以不需要等到線程創建就能立即執行。 提高線程的可管理性:線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。 用法:
-

26. ThreadLocal的設計理念與作用

ThreadLocal不是用來解決對象共享訪問問題的,而主要是提供了保持對象的方法和避免參數傳遞的方便的對象訪問方式。

每個線程中都有一個自己的ThreadLocalMap類對象,可以將線程自己的對象保持到其中,各管各的,線程可以正確的訪問到自己的對象。 將一個ThreadLocal靜態實例作為key,將不同對象的引用保存到不同線程的ThreadLocalMap中,然後在線程執行的各處通過這個靜態ThreadLocal實例的get()方法取得自己線程保存的那個對象,避免了將這個對象作為參數傳遞的麻煩。 ThreadLocal的應用場合,我覺得最適合的是多線程多實例(每個線程對應一個實例)的對象的訪問,並且這個對象很多地方都要用到。

27. Concurrent包裡的其它東西

28. wait()和sleep()的區別

這兩個方法來自不同的類分別是,sleep來自Thread類,和wait來自Object類 sleep是Thread的靜態類方法,誰調用的誰去睡覺,即使在a線程裡調用b的sleep方法,實際上還是a去睡覺,要讓b線程睡覺要在b的代碼中調用sleep。 鎖: 最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。
sleep不出讓系統資源;wait是進入線程等待池等待,出讓系統資源,其他線程可以使用鎖。一般wait不會加時間限制,因為如果wait線程的運行資源不夠,再出來也沒用,要等待其他線程調用notify/notifyAll喚醒等待池中的所有線程,才會進入就緒隊列等待OS分配系統資源。sleep()可以用時間指定使它自動喚醒過來,如果時間不到只能調用interrupt()強行打斷。 Thread.sleep(0)的作用是“觸發操作系統立刻重新進行一次CPU競爭”。 使用范圍:wait,notify和notifyAll只能在同步控制方法或者同步控制塊裡面使用,而sleep可以在任何地方使用。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved