Java Thread Overview

線程的創(chuàng)建#

創(chuàng)建線程有兩種方式:繼承Thread類,或者實(shí)現(xiàn)Runnable接口

繼承Thread類##

public class MyThread extends Thread{
    public void run(){
        System.out.println("MyThread running");
    }
}

MyThread myThread = new MyThread();
myThread.start();

也可以創(chuàng)建一個(gè)匿名類繼承自Thread

Thread thread = new Thread(){
    public void run(){
        System.out.println("Thread running");
    }
}

thread.start();

實(shí)現(xiàn)Runnable接口##

public class MyRunnable implements Runnable{
    public void run(){
        System.out.println("MyRunnable running");
    }
}

Thread myThread = new Thread(new MyRunnable());
myThread.start();

常見(jiàn)錯(cuò)誤:start()而不是run()##

不管實(shí)現(xiàn)Runnable接口還是繼承自Thread類,通過(guò)start方法去啟動(dòng)該線程。如果調(diào)用的是run方法,其實(shí)是在當(dāng)前線程執(zhí)行的。
這可以通過(guò)下面的例子驗(yàn)證


public class MyThread extends Thread{

    public void run(){
        System.out.println(Thread.currentThread() + ": running 1");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread() + ": running 2");
    }
    
    public static void main(String[] args) throws InterruptedException {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        thread1.run();
        thread2.run();
        thread1.join();
        thread2.join();
    }
}

輸出結(jié)果是

Thread[main,5,main]: running 1
Thread[main,5,main]: running 2
Thread[main,5,main]: running 1
Thread[main,5,main]: running 2

而如果把run改為start,輸出結(jié)果才符合我們的預(yù)期

Thread[Thread-0,5,main]: running 1
Thread[Thread-1,5,main]: running 1
Thread[Thread-0,5,main]: running 2
Thread[Thread-1,5,main]: running 2

線程安全#

下圖描述了java內(nèi)存模型


java-memory-model.png

多線程之間可能會(huì)共享變量。當(dāng)他們同時(shí)修改一個(gè)共享變量時(shí),就會(huì)產(chǎn)生race condition。這時(shí)候,可以使用synchronized關(guān)鍵字或者Lock避免race condition。

synchronized##

a synchronized block guarantees that only one thread can enter a given critial section. synchronized block also guarantee that all the variables accessed inside the synchronized block will be read in from main memory, and when the thread exists synchronized block, all update variables will be flushed back to main memory again.

synchronized關(guān)鍵字可以用來(lái)標(biāo)記四中不同的block。

  • instance method
  • static method
  • code blocks inside instance method
  • code blocks inside static method
  public class MyClass {
  
    public synchronized void log1(String msg1, String msg2){
       log.writeln(msg1);
       log.writeln(msg2);
    }

  
    public void log2(String msg1, String msg2){
       synchronized(this){
          log.writeln(msg1);
          log.writeln(msg2);
       }
    }
  }

Lock##

Lock其實(shí)是用synchronized關(guān)鍵字實(shí)現(xiàn)的。下面是一個(gè)簡(jiǎn)單的實(shí)現(xiàn)

public class Lock{
    private boolean isLocked = false;

    public synchronized void lock()
        throws InterruptedException{
        while(isLocked){
          wait();
        }
        isLocked = true;
    }

    public synchronized void unlock(){
        isLocked = false;
        notify();
    }
}

java.util.concurrent.Lock包中,Lock是一個(gè)接口,有以下幾個(gè)方法

  • lock()
  • lockInterruptibly()
  • trylock()
  • trylock(long timeout, TimeUnit timeUnit)
  • unlock()

實(shí)現(xiàn)Lock接口的類有ReentrantLock和ReadWriteLock。
ReentrantLock就是普通的lock。
ReadWriteLock實(shí)現(xiàn)了多讀,單寫(xiě)。

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

readWriteLock.readLock().lock();
// multiple readers can enter this section
// if not locked for writing, and not writers waiting
// to lock for writing.
readWriteLock.readLock().unlock();

readWriteLock.writeLock().lock();
// only one writer can enter this section,
// and only if no threads are currently reading.
readWriteLock.writeLock().unlock()

synchronized和Lock##

synchronized和Lock還是有些區(qū)別的:

  • 如果有多個(gè)線程等待,synchronized block不保證這些線程進(jìn)入的順序就是他們到達(dá)等待的順序
  • 因?yàn)椴荒軅鬟f參數(shù)給synchronized block,因此沒(méi)法設(shè)置timeout時(shí)間
  • synchronized block必須在一個(gè)函數(shù)體內(nèi)。但是lock和unlock可以在不同的函數(shù)體內(nèi)。

線程安全的容器##

java.util.concurrent包里面提供了很多線程安全的容器,可以讓我們?cè)诙嗑€程編程的時(shí)候更加容易。

  • BlockingQueue
  • ArrayBlockingQueue
  • LinkedBlockingQueue
  • DelayQueue
  • PriorityBlockingQueue
  • SynchronousQueue
  • BlockingDeque
  • LinkedBlockingDeque
  • ConcurrentMap
  • ConcurrentNavigableMap

線程安全的類##

多線程使得任何操作都變得如履薄冰。比如,你想對(duì)一個(gè)int類型的共享變量進(jìn)行++操作,那么傳統(tǒng)的 i++不是線程安全的。因?yàn)檫@句并不是原子操作。因?yàn)閖ava給我們提供了一些原子操作類。

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong
  • AtomicReference
  • AtomicStampedReference
  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReferenceArray

線程同步#

wait & notify##

java.lang.Object定義了三個(gè)方法 wait(), notify(), notifyAll()。通過(guò)這幾個(gè)方法,可以讓一個(gè)線程等待一個(gè)信號(hào),也可以讓另外一個(gè)線程發(fā)送一個(gè)信號(hào)。調(diào)用了wait()的線程會(huì)貶稱inactive狀態(tài),直到另外一個(gè)線程調(diào)用notify()。
需要注意的是,調(diào)用wait或者notify的線程必須要獲得這個(gè)對(duì)象的鎖。也就是說(shuō),一個(gè)線程必須在synchronized block中調(diào)用wait notify。

public class MonitorObject{
}

public class MyWaitNotify{
  MonitorObject myMonitorObject = new MonitorObject();

  public void doWait(){
    synchronized(myMonitorObject){
      try{
        myMonitorObject.wait();
      } catch(InterruptedException e){...}
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      myMonitorObject.notify();
    }
  }
}

Wouldn't the waiting thread keep the lock on the monitor object (myMonitorObject) as long as it is executing inside a synchronized block? Will the waiting thread not block the notifying thread from ever entering the synchronized block in doNotify()? The answer is no. Once a thread calls wait() it releases the lock it holds on the monitor object. This allows other threads to call wait() or notify() too, since these methods must be called from inside a synchronized block.
Once a thread is awakened it cannot exit the wait() call until the thread calling notify() has left its synchronized block. In other words: The awakened thread must reobtain the lock on the monitor object before it can exit the wait() call, because the wait call is nested inside a synchronized block. If multiple threads are awakened using notifyAll() only one awakened thread at a time can exit the wait() method, since each thread must obtain the lock on the monitor object in turn before exiting wait().

semaphore##

semaphore實(shí)現(xiàn)的功能就類似廁所有5個(gè)坑,假如有10個(gè)人要上廁所,那么同時(shí)只能有多少個(gè)人去上廁所呢?同時(shí)只能有5個(gè)人能夠占用,當(dāng)5個(gè)人中 的任何一個(gè)人讓開(kāi)后,其中等待的另外5個(gè)人中又有一個(gè)人可以占用了。

Semaphore semaphore = new Semaphore(5);

//critical section
semaphore.acquire();
...
semaphore.release();

除了acquire和release,Semaphor還提供了其他接口:

  • availablePermits()
  • acquireUninterruptibly()
  • drainPermits()
  • hasQueuedThreads()
  • getQueuedThreads()
  • tryAcquire()

CountDownLatch##

CyclicBarrier##

CyclicBarrier要做的事情是讓一組線程到達(dá)一個(gè)屏障時(shí)被阻塞,直到最后一個(gè)線程到達(dá)屏障時(shí),屏障門才會(huì)打開(kāi)。

public class CyclicBarrierTest {

    static CyclicBarrier c = new CyclicBarrier(2);

    public static void main(String[] args) {
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    c.await();
                } catch (Exception e) {

                }
                System.out.println(1);
            }
        }).start();

        try {
            c.await();
        } catch (Exception e) {

        }
        System.out.println(2);
    }
}

線程池#

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容