1.定义
原子操作(atomic operation)是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程切换。
2.非原子操作i++
在Java里面,++i(i++)或者--i(i--)不是线程安全的,这里面有三个独立的操作:获得变量当前值,为该值+1/-1,然后写回新的值. 通常情况下,只能使用加锁才能保证读-改-写这三个操作是"原子性"的。也就是说我们平时所用的i++等操作是非原子操作,一般的需要加锁才能保证其"原子性"
例如:
public static void main(String[] args) {
class Run implements Runnable{
private int i;
public void run() {
for(int j=0;j<100000;j++){
i++;
}
}
public int getValue(){
return this.i;
}
};
Run run = new Run();
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
Thread t3 = new Thread(run);
Thread t4 = new Thread(run);
t1.start();
t2.start();
t3.start();
t4.start();
try {
t1.join();
t2.join();
t3.join();
t4.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("i = "+run.getValue());
}
注:4个线程分别运行for循环10万次,每次i++,i是共享变量,那么最终i的值应该是40万,但是因为i++并非原子操作,所以最终结果也不一定是40万。
注:这里即使使用了volatile修饰共享变量i,也是不行的,因为volatile只是强制线程去主内存中取值和存值,而不能保证i++操作的原子性
3.加锁可以保证i++的原子性
例如:
public static void main(String[] args) {
class Run implements Runnable{
private int i;
public void run() {
for(int j=0;j<100000;j++){
i++;
}
}
public int getValue(){
return this.i;
}
};
Run run = new Run();
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
Thread t3 = new Thread(run);
Thread t4 = new Thread(run);
t1.start();
t2.start();
t3.start();
t4.start();
try {
t1.join();
t2.join();
t3.join();
t4.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("i = "+run.getValue());
}
4.原子操作类
JDK5.0提供了一组atomic-class来帮助我们简化同步处理。基本工作原理是使用了同步synchronized的方法实现了对一个long, int等类型值的增、减、赋值(更新)操作.
java.util.concurrent.atomic.AtomicXxxxx类
这些类都是类似的使用方法,这里以AtomicInteger为例:
i.get()就是获得该对象对应的int值
i.incrementAndGet() 类似于 ++i
i.getAndIncrement() 类似于 i++
i.getAndDecrement() 类似于 i--
i.decrementAndGet() 类似于 --i
例如:上面的例子可以修改如下
public static void main(String[] args) {
class Run implements Runnable{
private AtomicInteger i = new AtomicInteger(0);
public void run() {
for(int j=0;j<100000;j++){
i.incrementAndGet();
}
}
public int getValue(){
return i.get();
}
};
Run run = new Run();
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
Thread t3 = new Thread(run);
Thread t4 = new Thread(run);
t1.start();
t2.start();
t3.start();
t4.start();
try {
t1.join();
t2.join();
t3.join();
t4.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("i = "+run.getValue());
}
注:保证操作的原子性,每次运行后的结果都是40万