大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

第一部分 Java基礎
第二部分 Java進階

Java多線程和并發面試題(附答案)第6題

● java.util.concurrent.atomic包

● AtomicBoolean原子性布爾

AtomicBoolean是java.util.concurrent.atomic包下的原子變量,這個包里面提供了一組原子類。其基本的特性就是在多線程環境下,當有多個線程同時執行這些類的實例包含的方法時,具有排它性,即當某個線程進入方法,執行其中的指令時,不會被其他線程打斷,而別的線程就像自旋鎖一樣,一直等到該方法執行完成,才由JVM從等待隊列中選擇一個另一個線程進入,這只是一種邏輯上的理解。實際上是借助硬件的相關指令來實現的,不會阻塞線程(或者說只是在硬件級別上阻塞了)。

AtomicBoolean,在這個Boolean值的變化的時候不允許在之間插入,保持操作的原子性。下面將解釋重點方法并舉例:

boolean compareAndSet(expectedValue, updateValue);

● 這個方法主要有兩個作用:

比較AtomicBoolean和expect的值,如果一致,執行方法內的語句。其實就是一個if語句。

把AtomicBoolean的值設成update,比較最要的是這兩件事是一氣呵成的,這連個動作之間不會被打斷,任何內部或者外部的語句都不可能在兩個動作之間運行。為多線程的控制提供了解決的方案。

● 下面我們從代碼上解釋:

首先我們看下在不使用 AtomicBoolean 情況下,代碼的運行情況:

package com.bjpowernode;

import java.util.concurrent.TimeUnit;

public class BarWorker implements Runnable {
    //靜態變量
    private static boolean exists = false;

    private String name;

    public BarWorker(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        if (!exists) {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e1) {
                // do nothing
            }
            exists = true;
            System.out.println(name + " enter");
            try {
                System.out.println(name + " working");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                // do nothing
            }
            System.out.println(name + " leave");
            exists = false;
        } else {
            System.out.println(name + " give up");
        }

    }
    
    public static void main(String[] args) {
        BarWorker bar1 = new BarWorker("bar1");
        BarWorker bar2 = new BarWorker("bar2");
        new Thread(bar1).start();
        new Thread(bar2).start();
    }
}

運行結果:

bar1 enter

bar2 enter

bar1 working

bar2 working

bar1 leave

bar2 leave

從上面的運行結果我們可看到,兩個線程運行時,都對靜態變量exists同時做操作,并沒有保證exists靜態變量的原子性,也就是一個線程在對靜態變量exists進行操作到時候,其他線程必須等待或不作為。等待一個線程操作完后,才能對其進行操作。

下面我們將靜態變量使用AtomicBoolean來進行操作。

package com.bjpowernode;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class BarWorker2 implements Runnable {
    //靜態變量使用 AtomicBoolean 進行操作
    private static AtomicBoolean exists = new AtomicBoolean(false);

    private String name;

    public BarWorker2(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        if (exists.compareAndSet(false, true)) {

            System.out.println(name + " enter");

            try {
                System.out.println(name + " working");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                // do nothing
            }
            System.out.println(name + " leave");
            exists.set(false);
        } else {
            System.out.println(name + " give up");
        }
    }

    public static void main(String[] args) {
        BarWorker2 bar1 = new BarWorker2("bar1");
        BarWorker2 bar2 = new BarWorker2("bar2");
        new Thread(bar1).start();
        new Thread(bar2).start();
    }
}

運行結果:

bar1 enter

bar1 working

bar2 give up

bar1 leave

可以從上面的運行結果看出僅僅一個線程進行工作,因為exists.compareAndSet(false,true)提供了原子性操作,比較和賦值操作組成了一個原子操作,中間不會提供可乘之機。使得一個線程操作,其他線程等待或不作為。

下面我們簡單介紹下AtomicBoolean的API

● 你可以這樣創建一個AtomicBoolean:

AtomicBoolean atomicBoolean = new AtomicBoolean();  

以上示例新建了一個默認值為false的AtomicBoolean。如果你想要為AtomicBoolean實例設置一個顯式的初始值,那么你可以將初始值傳給AtomicBoolean的構造子:

AtomicBoolean atomicBoolean = new AtomicBoolean(true); 

● 獲得AtomicBoolean的值:

你可以通過使用get()方法來獲取一個AtomicBoolean的值。示例如下:

AtomicBoolean atomicBoolean = new AtomicBoolean(true);
boolean value = atomicBoolean.get();

● 設置AtomicBoolean的值:

你可以通過使用set()方法來設置一個AtomicBoolean的值。示例如下:

AtomicBoolean atomicBoolean = new AtomicBoolean(true);
atomicBoolean.set(false);

以上代碼執行后AtomicBoolean的值為false。

● 交換AtomicBoolean的值:

你可以通過 getAndSet()方法來交換一個AtomicBoolean實例的值。getAndSet()方法將返回AtomicBoolean當前的值,并將為AtomicBoolean設置一個新值。示例如下:

AtomicBoolean atomicBoolean = new AtomicBoolean(true);
boolean oldValue = atomicBoolean.getAndSet(false);

以上代碼執行后oldValue變量的值為true,atomicBoolean實例將持有false值。代碼成功將AtomicBoolean當前值ture交換為false。

● 比較并設置 AtomicBoolean 的值:

compareAndSet()方法允許你對AtomicBoolean的當前值與一個期望值進行比較,如果當前值等于期望值的話,將會對AtomicBoolean設定一個新值。compareAndSet()方法是原子性的,因此在同一時間之內有單個線程執行它。因此compareAndSet()方法可被用于一些類似于鎖的同步的簡單實現。以下是一個compareAndSet()示例:

AtomicBoolean atomicBoolean = new AtomicBoolean(true);
boolean expectedValue = true; boolean newValue = false;
boolean wasNewValueSet = atomicBoolean.compareAndSet(expectedValue, newValue);

本示例對AtomicBoolean的當前值與true值進行比較,如果相等,將AtomicBoolean的值更新為false。

● AtomicInteger原子性整型

AtomicInteger,一個提供原子操作的Integer的類。在Java語言中,++i和i++操作并不是線程安全的,在使用的時候,不可避免的會用到synchronized關鍵字。而AtomicInteger則通過一種線程安全的加減操作接口。

我們先來看看AtomicInteger給我們提供了什么方法:

ublic final int get() //獲取當前的值
public final int getAndSet(int newValue)//獲取當前的值,并設置新的值
public final int getAndIncrement()//獲取當前的值,并自增
public final int getAndDecrement() //獲取當前的值,并自減
public final int getAndAdd(int delta) //獲取當前的值,并加上預期的值

下面通過兩個簡單的例子來看一下AtomicInteger的優勢在哪?

● 普通線程同步:

class Test2 {
    private volatile int count = 0;
    public synchronized void increment() {
        count++; //若要線程安全執行執行 count++,需要加鎖
    }

    public int getCount() {
        return count;
    }
}

● 使用AtomicInteger:

import java.util.concurrent.atomic.AtomicInteger;

class Test2 {
    private AtomicInteger count = new AtomicInteger();
    public void increment() {
        count.incrementAndGet();
    }
    //使用 AtomicInteger 之后,不需要加鎖,也可以實現線程安全。
    public int getCount() {
        return count.get();
    }
}

從上面的例子中我們可以看出:使用AtomicInteger是非常安全的,而且因為AtomicInteger由硬件提供原子操作指令實現的,在非激烈競爭的情況下,開銷更小,速度更快。AtomicInteger是使用非阻塞算法來實現并發控制的。AtomicInteger的關鍵域只有以下3個:

// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
    try {
        valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) {
        throw new Error(ex);
    }
}

private volatile int value;

這里,unsafe是java提供的獲得對對象內存地址訪問的類,注釋已經清楚的寫出了,它的作用就是在更新操作時提供“比較并替換”的作用。實際上就是AtomicInteger中的一個工具。valueOffset是用來記錄value本身在內存的偏移地址的,這個記錄也主要是為了在更新操作在內存中找到value的位置,方便比較。

注意:value是用來存儲整數的時間變量,這里被聲明為volatile,就是為了保證在更新操作時,當前線程可以拿到value最新的值(并發環境下,value可能已經被其他線程更新了)。

優點:最大的好處就是可以避免多線程的優先級倒置和死鎖情況的發生,提升在高并發處理下的性能。

● 下面我們簡單介紹下 AtomicInteger 的 API

● 創建一個 AtomicInteger 示例如下:

AtomicInteger atomicInteger = new AtomicInteger();

本示例將創建一個初始值為0的AtomicInteger。如果你想要創建一個給

定初始值的AtomicInteger,你可以這樣:

AtomicInteger atomicInteger = new AtomicInteger(123);

本示例將123作為參數傳給AtomicInteger的構造子,它將設置AtomicInteger實例的初始值為123。

● 獲得AtomicInteger的值

你可以使用get()方法獲取AtomicInteger實例的值。示例如下:

AtomicInteger atomicInteger = new AtomicInteger(123);
int theValue = atomicInteger.get();

● 設置AtomicInteger的值

你可以通過set()方法對AtomicInteger的值進行重新設置。以下是AtomicInteger.set()示例:

AtomicInteger atomicInteger = new AtomicInteger(123);
atomicInteger.set(234);

以上示例創建了一個初始值為123的AtomicInteger,而在第二行將其值更新為234。

● 比較并設置AtomicInteger的值

AtomicInteger類也通過了一個原子性的compareAndSet()方法。這一方法將AtomicInteger實例的當前值與期望值進行比較,如果二者相等,為AtomicInteger實例設置一個新值。 AtomicInteger.compareAndSet()代碼示例:

AtomicInteger atomicInteger = new AtomicInteger(123);
int expectedValue = 123;
int newValue = 234;
atomicInteger.compareAndSet(expectedValue,newValue);

本示例首先新建一個初始值為123的AtomicInteger實例。然后將AtomicInteger與期望值123進行比較,如果相等,將AtomicInteger的值更新為234。

● 增加AtomicInteger的值

AtomicInteger類包含有一些方法,通過它們你可以增加AtomicInteger的值,并獲取其值。這些方法如下:

public final int addAndGet(int addValue)//在原來的數值上增加新的值,并返回新值
public final int getAndIncrement()//獲取當前的值,并自增
public final int incrementAndget() //自減,并獲得自減后的值
public final int getAndAdd(int delta) //獲取當前的值,并加上預期的值

第一個addAndGet()方法給AtomicInteger增加了一個值,然后返回增加后的值。getAndAdd()方法為AtomicInteger增加了一個值,但返回的是增加以前的AtomicInteger的值。具體使用哪一個取決于你的應用場景。以下是這兩種方法的示例:

AtomicInteger atomicInteger = new AtomicInteger();
System.out.println(atomicInteger.getAndAdd(10));
System.out.println(atomicInteger.addAndGet(10));

本示例將打印出0和20。例子中,第二行拿到的是加10之前的AtomicInteger的值。加10之前的值是0。第三行將AtomicInteger的值再加10,并返回加操作之后的值。該值現在是為20。你當然也可以使用這倆方法為AtomicInteger添加負值。結果實際是一個減法操作。getAndIncrement()和incrementAndGet()方法類似于getAndAdd()和addAndGet(),但每次只將AtomicInteger的值加1。

● 減小AtomicInteger的值

AtomicInteger類還提供了一些減小AtomicInteger的值的原子性方法。這些方法是:

public final int decrementAndGet()
public final int getAndDecrement()

decrementAndGet()將AtomicInteger的值減一,并返回減一后的值。getAndDecrement()也將AtomicInteger的值減一,但它返回的是減一之前的值。

● AtomicIntegerArray原子性整型數組

java.util.concurrent.atomic.AtomicIntegerArray類提供了可以以原子方式讀取和寫入的底層int數組的操作,還包含高級原子操作。AtomicIntegerArray支持對底層int數組變量的原子操作。它具有獲取和設置方法,如在變量上的讀取和寫入。也就是說,一個集合與同一變量上的任何后續get相關聯。原子compareAndSet方法也具有這些內存一致性功能。

AtomicIntegerArray本質上是對int[]類型的封裝。使用Unsafe類通過CAS的方式控制int[]在多線程下的安全性。它提供了以下幾個核心API:

//獲得數組第 i 個下標的元素
public final int get(int i)
//獲得數組的長度
public final int length()
//將數組第 i 個下標設置為 newValue,并返回舊的值
public final int getAndSet(int i, int newValue)
//進行 CAS 操作,如果第 i 個下標的元素等于 expect,則設置為 update,設置成功返回 true 
public final boolean compareAndSet(int i, int expect, int update)
//將第 i 個下標的元素加 1
public final int getAndIncrement(int i)
//將第 i 個下標的元素減 1
public final int getAndDecrement(int i)
//將第 i 個下標的元素增加 delta(delta 可以是負數)
public final int getAndAdd(int i,int delta)

下面給出一個簡單的示例,展示 AtomicIntegerArray 使用:

public class AtomicIntegerArrayDemo {
    static AtomicIntegerArray arr = new AtomicIntegerArray(10);
    public static class AddThread implements Runnable {
        public void run() {
            for (int k = 0; k < 10000; k++) 
	     arr.getAndIncrement(k % arr.length());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread[] ts = new Thread[10];
        for (int k = 0; k < 10; k++) {
            ts[k] = new Thread(new AddThread());
        }
        for (int k = 0; k < 10; k++) {
            ts[k].start();
        }
        for (int k = 0; k < 10; k++) {
            ts[k].join();
        }
        System.out.println(arr);
    }
}

輸出結果:

[10000,10000,10000,10000,10000,10000,10000,10000,10000,10000]

上述代碼第2行,申明了一個內含10個元素的數組。第3行定義的線程對數組內10個元素進行累加操作,每個元素各加1000次。第11行,開啟10個這樣的線程。因此,可以預測,如果線程安全,數組內10個元素的值必然都是10000。反之,如果線程不安全,則部分或者全部數值會小于10000。

● AtomicLong、AtomicLongArray原子性整型數組

AtomicLong、AtomicLongArray的API跟AtomicInteger、AtomicIntegerArray在使用方法都是差不多的。區別在于用前者是使用原子方式更新的long值和long數組,后者是使用原子方式更新的Integer值和Integer數組。兩者的相同處在于它們此類確實擴展了Number,允許那些處理基于數字類的工具和實用工具進行統一訪問。在實際開發中,它們分別用于不同的場景。這個就具體情況具體分析了,下面將舉例說明AtomicLong的使用場景(使用AtomicLong生成自增長ID)。

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

public class AtomicLongTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        final AtomicLong orderIdGenerator = new AtomicLong(0);
        final List<Item> orders = Collections
                .synchronizedList(new ArrayList<Item>());
        for (int i = 0; i < 10; i++) {
            Thread orderCreationThread = new Thread(new Runnable() {
                public void run() {
                    for (int i = 0; i < 10; i++) {
                        long orderId = orderIdGenerator.incrementAndGet();
                        Item order = new Item(Thread.currentThread().getName(), orderId);
                        orders.add(order);
                    }
                }
            });
            orderCreationThread.setName("Order Creation Thread " + i);
            orderCreationThread.start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Set<Long> orderIds = new HashSet<Long>();
        for (Item order : orders) {
            orderIds.add(order.getID());
            System.out.println("Order name:" + order.getItemName()
                    + "----" + "Order id:" + order.getID());
        }
    }
}

class Item {
    String itemName;
    long id;

    Item(String n, long id) {
        this.itemName = n;
        this.id = id;
    }

    public String getItemName() {
        return itemName;
    }

    public long getID() {
        return id;
    }
}

輸出:

Order name:Order Creation Thread 0----Order id:1

Order name:Order Creation Thread 1----Order id:2

Order name:Order Creation Thread 0----Order id:4

Order name:Order Creation Thread 1----Order id:5

Order name:Order Creation Thread 3----Order id:3

Order name:Order Creation Thread 0----Order id:7

Order name:Order Creation Thread 1----Order id:6

........

Order name:Order Creation Thread 2----Order id:100

從運行結果我們看到,不管是哪個線程。它們獲得的ID是不會重復的,保證的ID生成的原子性,避免了線程安全上的問題。

全部教程
主站蜘蛛池模板: 天天玩天天操 | 日韩欧美中 | 中文字幕专区在线亚洲 | 国产高清精品91在线 | 精品福利在线观看 | 亚洲图片综合区另类图片 | 天天做人人爱夜夜爽2020毛片 | 免费精品精品国产欧美在线 | 国产日本欧美亚洲精品视 | 国产精品久久毛片蜜月 | 来自深渊在线观看 | 91精品国产福利在线观看性色 | 天天射天天干天天 | 国产伦精品一区二区免费 | 成人国产在线24小时播放视频 | 久久在线综合 | 免费h片网站 | 久久精品国内偷自一区 | 在线视频亚洲 | 精品无人区乱码一区二区 | 亚洲精品欧美日韩 | c看欧美激情毛片 | 国产91久久最新观看地址 | 国产美女福利视频 | 国产在线综合视频 | 777xxxxx欧美| 久久男人资源站 | www午夜 | 黄色资源在线观看 | 亚洲日韩精品欧美一区二区 | 四虎4hu影库免费永久国产 | 欧美亚洲桃花综合 | 精品久久影院 | 日本欧美日韩 | 成人免费网站久久久 | 欧美日韩无线码在线观看 | 欧美精品日本一级特黄 | 中文字幕亚洲精品第一区 | 久久91综合国产91久久精品 | 激性欧美激情在线播放16页 | 国产成人99精品免费观看 |