Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作.

一、Atomic包下的所有类如下表:

类摘要
AtomicBoolean 可以用原子方式更新的 boolean 值。
AtomicInteger 可以用原子方式更新的 int 值。
AtomicIntegerArray 可以用原子方式更新其元素的 int 数组。
AtomicIntegerFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。
AtomicLong 可以用原子方式更新的 long 值。
AtomicLongArray 可以用原子方式更新其元素的 long 数组。
AtomicLongFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。
AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。
AtomicReference<V> 可以用原子方式更新的对象引用。
AtomicReferenceArray<E> 可以用原子方式更新其元素的对象引用数组。
AtomicReferenceFieldUpdater<T,V> 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。
AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。

二、AtomicInteger源码分析和基本的方法使用:

Atomicinteger类中的方法列表:

构造方法摘要
AtomicInteger() 
          创建具有初始值 0 的新 AtomicInteger。
AtomicInteger(int initialValue) 
          创建具有给定初始值的新 AtomicInteger。
方法摘要
 int addAndGet(int delta) 
          以原子方式将给定值与当前值相加。
 boolean compareAndSet(int expect, int update) 
          如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
 int decrementAndGet() 
          以原子方式将当前值减 1。
 double doubleValue() 
          以 double 形式返回指定的数值。
 float floatValue() 
          以 float 形式返回指定的数值。
 int get() 
          获取当前值。
 int getAndAdd(int delta) 
          以原子方式将给定值与当前值相加。
 int getAndDecrement() 
          以原子方式将当前值减 1。
 int getAndIncrement() 
          以原子方式将当前值加 1。
 int getAndSet(int newValue) 
          以原子方式设置为给定值,并返回旧值。
 int incrementAndGet() 
          以原子方式将当前值加 1。
 int intValue() 
          以 int 形式返回指定的数值。
 void lazySet(int newValue) 
          最后设置为给定值。
 long longValue() 
          以 long 形式返回指定的数值。
 void set(int newValue) 
          设置为给定值。
 String toString() 
          返回当前值的字符串表示形式。
 boolean weakCompareAndSet(int expect, int update) 
          如果当前值 == 预期值,则以原子方式将该设置为给定的更新值。
从类 java.lang.Number 继承的方法
byteValueshortValue
从类 java.lang.Object 继承的方法
cloneequalsfinalizegetClasshashCodenotifynotifyAllwaitwaitwait

AtomicInteger源码:

/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/ /*
*
*
*
*
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/ package java.util.concurrent.atomic;
import sun.misc.Unsafe; /**
* An {@code int} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
* {@code AtomicInteger} is used in applications such as atomically
* incremented counters, and cannot be used as a replacement for an
* {@link java.lang.Integer}. However, this class does extend
* {@code Number} to allow uniform access by tools and utilities that
* deal with numerically-based classes.
*
* @since 1.5
* @author Doug Lea
*/
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L; // 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; /**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
} /**
* Creates a new AtomicInteger with initial value {@code 0}.
*/
public AtomicInteger() {
} /**
* Gets the current value.
*
* @return the current value
*/
public final int get() {
return value;
} /**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(int newValue) {
value = newValue;
} /**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
} /**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
* and does not provide ordering guarantees, so is only rarely an
* appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} /**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
} /**
* Atomically decrements by one the current value.
*
* @return the updated value
*/
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
} /**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;
}
} /**
* Returns the String representation of the current value.
* @return the String representation of the current value.
*/
public String toString() {
return Integer.toString(get());
} public int intValue() {
return get();
} public long longValue() {
return (long)get();
} public float floatValue() {
return (float)get();
} public double doubleValue() {
return (double)get();
} }

AtomicInteger

其中的属性:

private static final long serialVersionUID = 6214790243416807050L;

    // 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中的sun.misc.Unsafe包,提供了安全访问内存的方法。这些方法提供给java访问底层的JNI(java-native-interface),因为这些方法最终是调用c/c++实现。
valueOffset:指向相对于对象起始位置的偏移量(内存中)可以理解为引用指向的内存,通过这个值可以去内存中查找某个引用在内存的值。
value:引用的当前值[预期值E]

方法示例:

public class AtomTest {
private static AtomicInteger num = new AtomicInteger();
public static void main(String[] args) {
System.out.println(num.compareAndSet(, ));//比较并设置,用0与内存中的值比较,相等的话,修改值为1
System.out.println(num.compareAndSet(, ));//比较并设置,用0与内存中的值比较,相等的话,修改值为1
System.out.println(num.get());;//获取初值
num.set();//设置初值,
System.out.println(num.get());
System.out.println(num.decrementAndGet());//获取值自减返回减1之后的值
System.out.println(num.addAndGet());//获取值添加2然后返回添加后的值
System.out.println(num.getAndIncrement());//获取值并且自增,返回的是自增前的值
System.out.println(num.getAndAdd());
System.out.println(num.getAndDecrement());
System.out.println(num.getAndSet());
System.out.println(num.get());
}
}

运行结果:

true
false

初始化的时候赋值为0,调用从compareAndSwap(0,1)之后返回true,compareAndSwap比较并交换,比较结果相同则修改值并返回TRUE不通则返回FALSE。

看看这个方法的源码:

/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

compareAndSwap:当前引用值和与要更新的值以及对象,和偏移量做参数去更新内存,根据对象和偏移量可以找到引用的内存值与引用值(期望值)作比较,相等则说明这个值在此对象获得后未被修改,那么我们就可以更新,则结果为true。这个方法就是传说中的CAS,CAS相较于synchonrized提供了一种无锁的线程安全保证。

示例中的前两个操作是一样的,我们可以理解为两个线程同时更新值为1,前一个快一点,发现更新的时候内存值未变则更新,等到第二个更新的时候原始值0与内存值(此时内存值变为1了)比较则失败返回FALSE并且不进行更新。这样就保证了一致性,而我们看到变量的值value使用voliate修饰的,保证了数据的内存可见性(一个线程修改其他线程可见)。所以多个线程并发的时候这个类代替Integer能够保证安全。

这个操作非常像数据库的乐观锁,给每个表添加一个version版本号,修改数据后要持久化先查询看版本号一致不,一致的话则持久化,不一致则重新获取值进行修改。

三、AtomicLong源码以及与AtomicInteger的不同:

package java.util.concurrent.atomic;
import sun.misc.Unsafe; /**
* A {@code long} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
* {@code AtomicLong} is used in applications such as atomically
* incremented sequence numbers, and cannot be used as a replacement
* for a {@link java.lang.Long}. However, this class does extend
* {@code Number} to allow uniform access by tools and utilities that
* deal with numerically-based classes.
*
* @since 1.5
* @author Doug Lea
*/
public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L; // setup to use Unsafe.compareAndSwapLong for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; /**
* Records whether the underlying JVM supports lockless
* compareAndSwap for longs. While the Unsafe.compareAndSwapLong
* method works in either case, some constructions should be
* handled at Java level to avoid locking user-visible locks.
*/
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8(); /**
* Returns whether underlying JVM supports lockless CompareAndSet
* for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
*/
private static native boolean VMSupportsCS8(); static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile long value; /**
* Creates a new AtomicLong with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicLong(long initialValue) {
value = initialValue;
} /**
* Creates a new AtomicLong with initial value {@code 0}.
*/
public AtomicLong() {
} /**
* Gets the current value.
*
* @return the current value
*/
public final long get() {
return value;
} /**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(long newValue) {
value = newValue;
} /**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(long newValue) {
unsafe.putOrderedLong(this, valueOffset, newValue);
} /**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final long getAndSet(long newValue) {
while (true) {
long current = get();
if (compareAndSet(current, newValue))
return current;
}
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
* and does not provide ordering guarantees, so is only rarely an
* appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
} /**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final long getAndIncrement() {
while (true) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
public final long getAndDecrement() {
while (true) {
long current = get();
long next = current - 1;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final long getAndAdd(long delta) {
while (true) {
long current = get();
long next = current + delta;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final long incrementAndGet() {
for (;;) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next))
return next;
}
} /**
* Atomically decrements by one the current value.
*
* @return the updated value
*/
public final long decrementAndGet() {
for (;;) {
long current = get();
long next = current - 1;
if (compareAndSet(current, next))
return next;
}
} /**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the updated value
*/
public final long addAndGet(long delta) {
for (;;) {
long current = get();
long next = current + delta;
if (compareAndSet(current, next))
return next;
}
} /**
* Returns the String representation of the current value.
* @return the String representation of the current value.
*/
public String toString() {
return Long.toString(get());
} public int intValue() {
return (int)get();
} public long longValue() {
return get();
} public float floatValue() {
return (float)get();
} public double doubleValue() {
return (double)get();
} }

AtomicLong源码

关于AtomicLong的用法和AtomicInteger类似:

不同之处在于多了下面一部分:一个静态的Boolean值VM_SUPPORTS_LONG_CAS,调用一个 native方法VMSupportCS8()返回虚拟机是否支持Long类型的无锁CAS机制

 /**
* Records whether the underlying JVM supports lockless
* compareAndSwap for longs. While the Unsafe.compareAndSwapLong
* method works in either case, some constructions should be
* handled at Java level to avoid locking user-visible locks.
*/
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8(); /**
* Returns whether underlying JVM supports lockless CompareAndSet
* for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
*/
private static native boolean VMSupportsCS8();

从这里可以看出不同的VM支持的数据类型可能有所差别,因为CAS需要硬件系统的支持。

四、AtomicReference源码及使用:

/**
* An object reference that may be updated atomically. See the {@link
* java.util.concurrent.atomic} package specification for description
* of the properties of atomic variables.
* @since 1.5
* @author Doug Lea
* @param <V> The type of object referred to by this reference
*/
public class AtomicReference<V> implements java.io.Serializable {
private static final long serialVersionUID = -1848883965231344442L; private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile V value; /**
* Creates a new AtomicReference with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicReference(V initialValue) {
value = initialValue;
} /**
* Creates a new AtomicReference with null initial value.
*/
public AtomicReference() {
} /**
* Gets the current value.
*
* @return the current value
*/
public final V get() {
return value;
} /**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(V newValue) {
value = newValue;
} /**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(V newValue) {
unsafe.putOrderedObject(this, valueOffset, newValue);
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
* and does not provide ordering guarantees, so is only rarely an
* appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
} /**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final V getAndSet(V newValue) {
while (true) {
V x = get();
if (compareAndSet(x, newValue))
return x;
}
} /**
* Returns the String representation of the current value.
* @return the String representation of the current value.
*/
public String toString() {
return String.valueOf(get());
} }

AtomicReference源码

除了类型是个对象的之外,方法使用和AtomicInteger一模一样:

AtomicReference的使用:

import java.util.concurrent.atomic.AtomicReference;

public class AtomTest {
private static AtomicReference<Pig> pigtest;
public static void main(String[] args) {
Pig pig = new Pig("猪坚强", 2);
Pig pig2 = new Pig("猪八戒", 2);
System.out.println("pig_hashCode:"+pig.hashCode());
System.out.println("pig2_hashCode:"+pig2.hashCode());
AtomTest.pigtest = new AtomicReference<Pig>(pig);
System.out.println(pigtest.get().toString());
System.out.println(pigtest.get().hashCode());
System.out.println(pigtest.compareAndSet(pig, pig2));
System.out.println(pigtest.compareAndSet(pig, pig2));
System.out.println(pigtest.get().toString());
System.out.println(pigtest.get().hashCode());
}
}

运行结果:

pig_hashCode:779824645
pig2_hashCode:420110874
[猪坚强,2]
779824645
true
false
[猪八戒,2]
420110874

第一步:先获取pigTest的toString(),和hashCode,结果是【猪坚强,2】,hashCode:779824645【我们发现hashCode居然==pig_hashCode】

第二步:进行CAS修改为pig2结果为true,再进行一次结果为FALSE,

第三步:获取pigTest的toString(),和hashCode,结果是【猪八戒,2】,hashCode:420110874【我们发现hashCode居然==pig2_hashCode】

结果说明了:pigtest是一个指向对象引用的引用,利用CAS操作可以修改pigTest指向的对象引用从而改变自身指向的对象。第一次CAS成功将pigtest指向的pig修改成了pig2.第二次CAS操作失败了,可以保证多线程并发时的安全问题。

五、总结:

  Atomic包下内容并不复杂,一句话来说就是提供了CAS无锁的安全访问机制。表现出来的是通过期望值E与内存值M作比较,相同则修改内存值M为更新值U。四个参数:当前对象this,偏移量V,期望值E,更新值U。

  利用CAS+voliate+native的机制保证数据操作的原子性,可见性和一致性。voliate使变量可见,CAS调用unsafe中的native方法访问系统底层的实现。unsafe中的这些方法直接操作内存,运用不当可能造成很大的问题。

  其实从Atomic包中的原子类的探索中,只是想引出CAS这个概念,CAS同样提供了一种线程安全的机制,而它不同于Synchonrized,synchonrized被称之为重量级锁,原因是因为粒度太强,加锁就代表着线程阻塞,高并发访问时带来的性能问题是硬伤。

  为了解决这种问题出现了两种机制:一种就是CAS,另一种是锁优化。

  CAS是将阻塞下降到了底层CPU上(纯属个人理解,因为看到有权威是说存在阻塞的,可能让别的线程知道已经更改了数据并且更新失败也是一种阻塞吧),语言层面访问效率远远低于系统内部硬件上,尽管同样是阻塞,在系统内部加锁解锁的效率要高很多。但是需要的是硬件支持,不过现在绝大部分CPU都已经支持CAS了。

  unsafe类:http://www.cnblogs.com/mickole/articles/3757278.html大家可以看这篇博客。

多线程爬坑之路-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析的更多相关文章

  1. [Java多线程]-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  2. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  3. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  4. 多线程爬坑之路-ThreadLocal源码及原理的深入分析

    ThreadLocal<T>类:以空间换时间提供一种多线程更快捷访问变量的方式.这种方式不存在竞争,所以也不存在并发的安全性问题. This class provides thread-l ...

  5. 多线程爬坑之路-Thread和Runable源码解析

    多线程:(百度百科借一波定义) 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提 ...

  6. 多线程爬坑之路--并发,并行,synchonrized同步的用法

    一.多线程的并发与并行: 并发:多个线程同时都处在运行中的状态.线程之间相互干扰,存在竞争,(CPU,缓冲区),每个线程轮流使用CPU,当一个线程占有CPU时,其他线程处于挂起状态,各线程断续推进. ...

  7. [Java多线程]-线程池的基本使用和部分源码解析(创建,执行原理)

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 多线 ...

  8. [Java多线程]-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  9. Vue 爬坑之路(六)—— 使用 Vuex + axios 发送请求

    Vue 原本有一个官方推荐的 ajax 插件 vue-resource,但是自从 Vue 更新到 2.0 之后,官方就不再更新 vue-resource 目前主流的 Vue 项目,都选择 axios ...

随机推荐

  1. react+redux构建淘票票首页

    react+redux构建淘票票首页 描述 在之前的项目中都是单纯的用react,并没有结合redux.对于中小项目仅仅使用react是可以的:但当项目变得更加复杂,仅仅使用react是远远不够的,我 ...

  2. 内功心法 -- java.util.ArrayList<E> (3)

    写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------下文主要对java.util ...

  3. asp.net权限认证:OWIN实现OAuth 2.0 之授权码模式(Authorization Code)

    asp.net权限认证系列 asp.net权限认证:Forms认证 asp.net权限认证:HTTP基本认证(http basic) asp.net权限认证:Windows认证 asp.net权限认证 ...

  4. Omi教程-组件通讯

    组件通讯 Omi框架组建间的通讯非常遍历灵活,因为有许多可选方案进行通讯: 通过在组件上声明 data-* 传递给子节点 通过在组件上声明 data 传递给子节点 父容器设置 childrenData ...

  5. app集成微信支付服务端代码-php版本

    1.微信支付分为两种,一种是微信公众品台的微信支付,另一种是微信开放平台的微信支付 2.上周做的是开放品台的微信支付,把遇到的问题总结一下 第一,下载官方提供的代码,解压后放到根目录下,然后认真读文档 ...

  6. 如何解决在chrome中自动完成表单后input出现黄色背景

    可以对input:-webkit-autofill使用足够大的纯色内阴影来覆盖input输入框的黄色背景:如: 代码如下: input:-webkit-autofill { -webkit-box-s ...

  7. [Spark] - Spark部署安装

    环境:centos6.0 虚拟机 搭建单机版本的spark 前提条件:搭建好hadoop环境 1. 下载scala进行安装 只需要设置环境变量SCALA_HOME和PATH即可 export SCAL ...

  8. IOS编程学习笔记

    @interface -实例对象 +类名 #import "MyClass" @implementation MyClass -(id)initWithString:(NSStri ...

  9. Protege5.0.0入门学习

    OWL本体的重要组成部分 Individuals:个体,代表一个领域里面的对象.可以理解成一个类的实例(instances of classes). Properties:属性,是两个个体之间的双重联 ...

  10. liunx常用命令

    查看系统信息常用命令 uname -m /arch      显示机器的处理架构 uname -r                显示正在使用的内核版本 cat/proc/cpuinfo      显 ...