高并發(fā)一

1、多線程的實現(xiàn)

多線程的實現(xiàn)方案一:繼承Thread類,重寫run()方法
java是單繼承,也就是說繼承本身是很寶貴。
多線程的實現(xiàn)方案二:實現(xiàn)Runnable接口
多線程程序?qū)崿F(xiàn)方案三:實現(xiàn)Callable接口

2、synchronized關鍵字

https://segmentfault.com/a/1190000003810166

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

public class ThreadTest implements Runnable{
    public static Integer num = 0;
   @Override
    public synchronized void run(){
       for(int i = 0;i<100;i++)
           System.out.println("hello" + num++);
   }
   public static void main(String[] args) throws{
       ExecutorService pool = Executors.newFixedThreadPool(2);
       pool.submit(new ThreadTest());
       pool.submit(new ThreadTest());
       pool.shutdown();
   }
}

上面使用了同步方法,可以控制線程獨占執(zhí)行體對象,這樣在執(zhí)行的過程中就可以使得線程將執(zhí)行體上的任務一次性執(zhí)行完后退出鎖定狀態(tài),JVM再調(diào)度另一個線程進來一次性運行執(zhí)行體內(nèi)的任務。實際執(zhí)行時可以發(fā)現(xiàn),沒有synchronized關鍵字運行出來的數(shù)據(jù)會少很多。

3、yield關鍵字

https://blog.csdn.net/dabing69221/article/details/17426953
使當前線程從執(zhí)行狀態(tài)(運行狀態(tài))變?yōu)榭蓤?zhí)行態(tài)(就緒狀態(tài))。cpu會從眾多的可執(zhí)行態(tài)里選擇,也就是說,當前也就是剛剛的那個線程還是有可能會被再次執(zhí)行到的,并不是說一定會執(zhí)行其他線程而該線程在下一次中不會執(zhí)行到了。
程序示例:

public class ThreadTest extends Thread{
    public static Integer num = 0;
    @Override
    public void run(){
       for(int i = 0;i<50;i++) {
           System.out.println("hello" + i);
           if(i==30){
               this.yield();
           }
       }
   }
   public static void main(String[] args) {
       ThreadTest t1 = new ThreadTest();
       ThreadTest t2 = new ThreadTest();
       t1.start();
       t2.start();
   }
}

需要注意的是yield是 Thread的方法

4、wait()和notify()方法

http://www.itdecent.cn/p/f4454164c017
http://www.itdecent.cn/p/f7d4819b7b24
下面是一個簡單的示例程序,說明了wait和notify的基本用法,

package com.company;

import java.util.concurrent.TimeUnit;

public class WaitNotifyCase {
    public static void main(String[] args){
        final Object lock = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread a is waiting to get lock");
                synchronized (lock){
                    try {
                        System.out.println("thread A get lock");
                        TimeUnit.SECONDS.sleep(1);
                        lock.wait();
                        System.out.println("wait end");
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread b is waiting to get lock");
                synchronized (lock){
                    try {
                        System.out.println("thread b get lock");
                        TimeUnit.SECONDS.sleep(5);
                        System.out.println("wait end");
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    lock.notify();
                    System.out.println("notify method");
                }
            }
        }).start();

    }
}

注意的是需要調(diào)用同一個對象的wait和notify,線程執(zhí)行到wait時會被掛起,直到notify通知繼續(xù)運行。
上面兩個線程都有synchronized關鍵字,為什么需要?還得從wait和notify本質(zhì)出發(fā),線程執(zhí)行l(wèi)ock.wait()方法時,必須持有該lock對象的monitor,如果wait方法在synchronized代碼中執(zhí)行,該線程很顯然已經(jīng)持有了monitor。在執(zhí)行wait時需要獲得monitor,但是執(zhí)行完之后就會釋放,進入等待狀態(tài)。從這個角度看,這也是為什么這邊synchronized 代碼沒有執(zhí)行完而那邊卻可以進入notify函數(shù),notify在synchronized代碼中執(zhí)行,先獲取到對象monitor,然后通知,但wait并不會立刻就執(zhí)行,因為還需要獲得monitor,所以只有synchronized 代碼塊執(zhí)行完畢之后wait才會繼續(xù)執(zhí)行。
使用wait、notify有一個需要特別注意,那就是不能讓notify先執(zhí)行,這樣wait就會一直阻塞。需要嚴格控制兩者執(zhí)行順序。

5、錯誤加鎖

public class ThreadTest implements Runnable{
   public static Integer i = 0;
   static ThreadTest instace = new ThreadTest();
   @Override
    public void run(){
       for(int j=0;j<100000;j++){
           synchronized (i) { i++;}
       }
   }
   public static void main(String[] args) throws InterruptedException{
       Thread t1 = new Thread(instace);
       Thread t2 = new Thread(instace);
       t1.start();
       t2.start();
       t1.join();
       t2.join();
       System.out.println(i);
   }
}

上面程序看起來沒有問題,但是打印出來的i確不是200000,得到了一個小很多的數(shù)字。涉及到一個自動裝包和拆包的問題。
執(zhí)行i++時,實際上是 i = Integer.valueOf(i.intValue()+1)
Integer.valueOf()實際上是一個工廠方法,會傾向于返回一個代表指定數(shù)值的Integer實例,創(chuàng)建一個新的對象(大于127),因此兩個線程每次加鎖可能加在不同的對象實例上。

6、volatile關鍵字

http://www.infoq.com/cn/articles/java-multi-thread-volatile
http://www.techug.com/post/java-volatile-keyword.html
被volatile修飾的共享變量,就具有了以下兩點特性:

  1. 保證了不同線程對該變量操作的內(nèi)存可見性;
  2. 禁止指令重排序

7、ReentrantLock

參考:https://my.oschina.net/hosee/blog/607677
ReentrantLock可以看作是synchronized的加強版,之前版本,ReentrantLock的性能要好于synchronized,由于對JVM進行了優(yōu)化,現(xiàn)在的JDK版本中,兩者性能是不相上下的。如果是簡單的實現(xiàn),不要刻意去使用ReentrantLock。
相比于synchronized,ReentrantLock在功能上更加豐富,它具有可重入、可中斷、可限時、公平鎖等特點。
簡單示例:

import java.util.concurrent.locks.ReentrantLock;

public class Test implements Runnable
{
    public static ReentrantLock lock = new ReentrantLock();
    public static int i = 0;

    @Override
    public void run()
    {
        for (int j = 0; j < 10000000; j++)
        {
            lock.lock();
            try
            {
                i++;
            }
            finally
            {
                lock.unlock();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException
    {
        Test test = new Test();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }

}

ReentrantLock是重入鎖,可以反復得到相同的一把鎖,它有一個與鎖相關的獲取計數(shù)器,如果擁有鎖的某個線程再次得到鎖,那么獲取計數(shù)器就加1,然后鎖需要被釋放兩次才能獲得真正釋放。如下:

lock.lock();
lock.lock();
try
{
    i++;
            
}           
finally
{
    lock.unlock();
    lock.unlock();
}

如果只是簡單的互斥鎖,上面這種情況就會發(fā)生死鎖,因為鎖只允許依次進入。
同樣,synchronize也是可重入鎖。

public class Child extends Father implements Runnable{
    final static Child child = new Child();//為了保證鎖唯一
    public static void main(String[] args) {
        for (int i = 0; i < 50; i++) {
            new Thread(child).start();
        }
    }
 
    public synchronized void doSomething() {
        System.out.println("1child.doSomething()");
        doAnotherThing(); // 調(diào)用自己類中其他的synchronized方法
    }
 
    private synchronized void doAnotherThing() {
        super.doSomething(); // 調(diào)用父類的synchronized方法
        System.out.println("3child.doAnotherThing()");
    }
 
    @Override
    public void run() {
        child.doSomething();
    }
}
class Father {
    public synchronized void doSomething() {
        System.out.println("2father.doSomething()");
    }
}

可以看到一個線程進入不同的 synchronized方法,是不會釋放之前得到的鎖的。
ReentrantLock還支持可中斷,定時和公平方式,功能比較豐富。

8、條件變量

package com.company;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest implements Runnable{
   public static ReentrantLock lock = new ReentrantLock();
   public static Condition condition = lock.newCondition();

   @Override
    public void run(){
       try{
           lock.lock();
           System.out.println("locked");
           condition.await();
           System.out.println("thread is going");
       }catch (InterruptedException e){
           e.printStackTrace();
       }finally {
           lock.unlock();
       }
   }
   public static void main(String[] args) throws InterruptedException{
       ThreadTest t1 = new ThreadTest();
       Thread th = new Thread(t1);
       th.start();
       Thread.sleep(2000);
       System.out.println("after sleep");
       lock.lock();
       condition.signal();
       Thread.sleep(1000);
       System.out.println("before unlock");
       lock.unlock();
   }
}

9、信號量

對于鎖來說,它是互斥的排他的。意思就是,只要我獲得了鎖,沒人能再獲得了。
而對于Semaphore來說,它允許多個線程同時進入臨界區(qū)。可以認為它是一個共享鎖,但是共享的額度是有限制的,額度用完了,其他沒有拿到額度的線程還是要阻塞在臨界區(qū)外。當額度為1時,就相等于lock。

package com.company;

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

public class semaphoreTest implements Runnable{
    final Semaphore semp = new Semaphore(5);
    @Override
    public void run(){
        try{
            semp.acquire();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getId()+":done");
            semp.release();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        ExecutorService exec = Executors.newFixedThreadPool(20);
        final semaphoreTest demo = new semaphoreTest();
        for(int i=0;i<20;i++){
            exec.submit(demo);
        }
    }
}

11、讀寫鎖

ReadWriteLock是區(qū)分功能的鎖。讀和寫是兩種不同的功能,讀-讀不互斥,讀-寫互斥,寫-寫互斥。適用于讀頻繁的場景。
這樣的設計是并發(fā)量提高了,又保證了數(shù)據(jù)安全。

private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock(); 
private static Lock readLock = readWriteLock.readLock(); 
private static Lock writeLock = readWriteLock.writeLock();

12、CountDownLatch和CyclicBarrier

倒數(shù)計數(shù)器CountDownLatch

表示執(zhí)行某任務前,其他指定任務必須完成。典型的場景就是火箭發(fā)射。在火箭發(fā)射前,為了保證萬無一失,往往還要進行各項設備、儀器的檢查。 只有等所有檢查完畢后,引擎才能點火。這種場景就非常適合使用CountDownLatch。它可以使得點火線程,等待所有檢查線程全部完工后,再執(zhí)行。

static final CountDownLatch end = new CountDownLatch(10);
end.countDown(); 
end.await();

示例

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

public class Test implements Runnable
{
    static final CountDownLatch countDownLatch = new CountDownLatch(10);
    static final Test t = new Test();
    @Override
    public void run()
    {
        try
        {
            Thread.sleep(2000);
            System.out.println("complete");
            countDownLatch.countDown();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) throws InterruptedException
    {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++)
        {
            executorService.execute(t);
        }
        countDownLatch.await();
        System.out.println("end");
        executorService.shutdown();
    }

}
CyclicBarrier

http://www.itdecent.cn/p/424374d71b67
和CountDownLatch相似,也是等待某些線程都做完以后再執(zhí)行。與CountDownLatch區(qū)別在于這個計數(shù)器可以反復使用。比如,假設我們將計數(shù)器設置為10。那么湊齊第一批1 0個線程后,計數(shù)器就會歸零,然后接著湊齊下一批10個線程

public CyclicBarrier(int parties, Runnable barrierAction) 
barrierAction就是當計數(shù)器一次計數(shù)完成后,系統(tǒng)會執(zhí)行的動作
await()
import java.util.concurrent.CyclicBarrier;

public class Test implements Runnable
{
    private String soldier;
    private final CyclicBarrier cyclic;

    public Test(String soldier, CyclicBarrier cyclic)
    {
        this.soldier = soldier;
        this.cyclic = cyclic;
    }

    @Override
    public void run()
    {
        try
        {
            //等待所有士兵到齊
            cyclic.await();
            dowork();
            //等待所有士兵完成工作
            cyclic.await();
        }
        catch (Exception e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    private void dowork()
    {
        // TODO Auto-generated method stub
        try
        {
            Thread.sleep(3000);
        }
        catch (Exception e)
        {
            // TODO: handle exception
        }
        System.out.println(soldier + ": done");
    }

    public static class BarrierRun implements Runnable
    {

        boolean flag;
        int n;

        public BarrierRun(boolean flag, int n)
        {
            super();
            this.flag = flag;
            this.n = n;
        }

        @Override
        public void run()
        {
            if (flag)
            {
                System.out.println(n + "個任務完成");
            }
            else
            {
                System.out.println(n + "個集合完成");
                flag = true;
            }

        }

    }

    public static void main(String[] args)
    {
        final int n = 10;
        Thread[] threads = new Thread[n];
        boolean flag = false;
        CyclicBarrier barrier = new CyclicBarrier(n, new BarrierRun(flag, n));
        System.out.println("集合");
        for (int i = 0; i < n; i++)
        {
            System.out.println(i + "報道");
            threads[i] = new Thread(new Test("士兵" + i, barrier));
            threads[i].start();
        }
    }

}

上面每個線程有兩個await,線程遇到await會阻塞,當指定數(shù)量的線程就緒,所有線程繼續(xù)運行。同時會觸發(fā) CyclicBarrier(n, new BarrierRun(flag, n))中第二個參數(shù)指定的線程。
CyclicBarrier比CountDownLatch更復雜,功能更強大。

13、LockSupport

LockSupport 提供線程阻塞原語
用法如下:

LockSupport.park(); 
LockSupport.unpark(t1);

回顧Thread中suspend,resume,stop方法,
suspend,使線程暫停,但是不會釋放類似鎖這樣的資源。
resume,使線程恢復,如果之前沒有使用suspend暫停線程,則不起作用。
stop,停止當前線程。不會保證釋放當前線程占有的資源。
suspend和resume也能提供暫停和繼續(xù)的,但是如果resume發(fā)生在suspend之前就會發(fā)生暫停線程得不到繼續(xù),而這種情況在多線程環(huán)境下很容易發(fā)生。
但LockSupport下的park和unpark就不會發(fā)生這樣的情況。

import java.util.concurrent.locks.LockSupport;
 
public class Test
{
    static Object u = new Object();
    static TestSuspendThread t1 = new TestSuspendThread("t1");
    static TestSuspendThread t2 = new TestSuspendThread("t2");
 
    public static class TestSuspendThread extends Thread
    {
        public TestSuspendThread(String name)
        {
            setName(name);
        }
 
        @Override
        public void run()
        {
            synchronized (u)
            {
                System.out.println("in " + getName());
                //Thread.currentThread().suspend();
                LockSupport.park();
            }
        }
    }
 
    public static void main(String[] args) throws InterruptedException
    {
        t1.start();
        Thread.sleep(100);
        t2.start();
//        t1.resume();
//        t2.resume();
        LockSupport.unpark(t1);
        LockSupport.unpark(t2);
        t1.join();
        t2.join();
    }
}
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容