### Atoms
Atoms 提供了一種比使用Refs&STM更簡單的更新當個值的方法。它不受事務的影響
有三個函數可以修改一個Atom的值: `reset!` , `compare-and-set!` 和 `swap!` .
`reset!` 函數接受兩個參數:要設值的Atom以及新值。它設置新的值,而不管你舊的值是什么。看例子:
```
(def my-atom (atom 1))
(reset! my-atom 2)
(println @my-atom) ; -> 2
```
`compare-and-set!` 函數接受三個參數:要被修改的Atom, 上次讀取時候的值,新的值。 這個函數在設置新值之前會去讀Atom現在的值。如果與上次讀的時候的值相等, 那么設置新值并返回true, 否則不設置新值, 返回false。看例子:
```
(def my-atom (atom 1))
(defn update-atom []
(let [curr-val @my-atom]
(println "update-atom: curr-val =" curr-val) ; -> 1
(Thread/sleep 50) ; give reset! time to run
(println
(compare-and-set! my-atom curr-val (inc curr-val))))) ; -> false
(let [thread (Thread. #(update-atom))]
(.start thread)
(Thread/sleep 25) ; give thread time to call update-atom
(reset! my-atom 3) ; happens after update-atom binds curr-val
(.join thread)) ; wait for thread to finish
(println @my-atom) ; -> 3
```
為什么最后的結果是 3呢? `update-atom` 被放在一個單獨的線程里面,在 `reset!` 函數調用之前執行。所以它獲取了atom的初始值1(存到變量curr-val里面去了), 然后它sleep了以讓 `reset!` 函數有執行是時間。在那之后,atom的值就變成3了。當 `update-atom` 函數調用 `compare-and-set!` 來給這個值加一的時候, 它發現atom的值已經不是它上次讀取的那個值了(1), 所以更新失敗, atom的值還是3。
`swap!` 函數接受一個要修改的 Atom, 一個計算Atom新值的函數以及一些額外的參數(如果需要的話)。這個計算Atom新的值的函數會以這個Atom以及一些額外的參數做為輸入。swap!函數實際上是對compare-and-set!函數的一個封裝,但是有一個顯著的不同。 它首先把Atom的當前值存入一個變量,然后調用計算新值的函數來計算新值, 然后再調用compare-and-set!函數來賦值。如果賦值成功的話,那就結束了。如果賦值不成功的話, 那么它會重復這個過程,一直到賦值成功為止。這就是它們的區別:所以上面的代碼可以用swap!改寫成這樣:
```
(def my-atom (atom 1))
(defn update-atom [curr-val]
(println "update-atom: curr-val =" curr-val)
(Thread/sleep 50) ; give reset! time to run
(inc curr-val))
(let [thread (Thread. #(swap! my-atom update-atom))]
(.start thread)
(Thread/sleep 25) ; give swap! time to call update-atom
(reset! my-atom 3)
(.join thread)) ; wait for thread to finish
(println @my-atom) ; -> 4
```
為什么輸出變成4了呢?因為swap!會不停的去給curr-val加一直到成功為止。