题:Java中 ++i 的操作是线程安全的么

作者&投稿:吴萧 (若有异议请与网页底部的电邮联系)
java int i; i++线程安全吗?为什么?~

i++是不安全的,因为java在操作i++的时候,是分步骤做的,可以理解为:
tp = i;
tp2 = i+1;
i=tp2;
如果线程1在执行第一条代码的时候,线程2访问i变量,这个时候,i的值还没有变化,还是原来的值,所以是不安全的。

JAVA中如何保证线程安全以及主键自增有序
一、常见场景
多个线程针对一个i进行主键自增。多线程下如果不做安全策略,将会导致各个现成获取的i值重复,导致脏数据
常见策略
1、增加syschroize进行线程同步
2、使用lock、unlock处理
3、使用reetrantent 锁进行锁定
缺点:容易造成性能低下,或者编写代码容易造成死锁
二、新方案
jdk新提供的功能,atomicInteger(还有其他一atomic开头的原子性操作类)
AtomicInteger,一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。
原理:通过java的CAS compare and swap,简称cas原语进行操作提升性能,这个也号称乐观锁,不阻塞
观锁实际上并不加锁,当计算遇到冲突或者说前后不一致时会重试 直到成功
CAS有3个操作数 内存值V 要跟内存值做比较的值A 和 新值 B
[html] view plain copy
while(true){
if(V == A){
V = B;
return ;
}else{
A = V;
}
}

CAS的操作对象为volatile类型volatile类型变量是:CPU直接读写变量所在的内存 而不是把变量copy到寄存器操作这样对变量的操作所有线程都是可见的这样做的结果是减少了并发时冲突的概率 但不能完全避免

总的结论:java是线程安全的,即对任何方法(包括静态方法)都可以不考虑线程冲突,但有一个前提,就是不能存在全局变量。如果存在全局变量,则需要使用同步机制。如下通过一组对比例子从头讲解:在多线程中使用静态方法会发生什么事?也就是说多线程访问同一个类的static静态方法会发生什么事?是否会发生线程安全问题?publicclassTest{publicstaticvoidoperation(){//dosomething}}事实证明只要在静态函数中没有处理多线程共享数据,就不存在着多线程访问同一个静态方法会出现资源冲突的问题。下面看一个例子:publicclassStaticThreadimplementsRunnable{@Overridepublicvoidrun(){//TODOAuto-generatedmethodstubStaticAction.print();}publicstaticvoidmain(String[]args){for(inti=0;i<100;i++){newThread(newStaticThread()).start();}}}publicclassStaticAction{publicstaticinti=0;publicstaticvoidprint(){intsum=0;for(inti=0;i<10;i++){System.out.print("step"+i+"isrunning.");sum+=i;}if(sum!=45){System.out.println("Threaderror!");System.exit(0);}System.out.println("sumis"+sum);}}实际执行的结果显示各个线程对静态方法的访问是交叉执行的,但是这并不影响各个线程静态方法print()中sum值的计算。也就是说,在此过程中没有使用全局变量的静态方法在多线程中是安全的,静态方法是否引起线程安全问题主要看该静态方法是否对全局变量(静态变量staticmember)进行修改操作。在多线程中使用同一个静态方法时,每个线程使用各自的实例字段(instancefield)的副本,而共享一个静态字段(staticfield)。所以说,如果该静态方法不去操作一个静态成员,只在方法内部使用实例字段(instancefield),不会引起安全性问题。但是,如果该静态方法操作了一个静态变量,则需要静态方法中采用互斥访问的方式进行安全处理。我们来看一下没有使用互斥访问的话会产生怎样的问题:publicclassStaticAction{publicstaticinti=0;publicstaticvoidincValue(){inttemp=StaticAction.i;try{Thread.sleep(1);}catch(Exceptione){e.printStackTrace();}temp++;StaticAction.i=temp;}}publicclassStaticThreadimplementsRunnable{@Overridepublicvoidrun(){//TODOAuto-generatedmethodstubStaticAction.incValue();}publicstaticvoidmain(String[]args){for(inti=0;i<100;i++){newThread(newStaticThread()).start();}try{Thread.sleep(1000);//预留足够的时间让上面的线程跑完}catch(Exceptione){e.printStackTrace();}System.out.println(StaticAction.i);}}实际运行结果显示i值为随机的数字。为了实现互斥访问,这时我们需要加入一个synchronized关键字。代码修改如下:publicclassStaticAction{publicstaticinti=0;publicsynchronizedstaticvoidincValue(){inttemp=StaticAction.i;try{Thread.sleep(1);}catch(Exceptione){e.printStackTrace();}temp++;StaticAction.i=temp;}}publicclassStaticThreadimplementsRunnable{@Overridepublicvoidrun(){//TODOAuto-generatedmethodstubStaticAction.incValue();}publicstaticvoidmain(String[]args){for(inti=0;i<100;i++){newThread(newStaticThread()).start();}try{Thread.sleep(1000);}catch(Exceptione){e.printStackTrace();}System.out.println(StaticAction.i);}}运行结果则必然是100。加入synchronized关键字的静态方法称为同步静态方法。在访问同步静态方法时,会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。这个其实就是操作系统中的用信号量实现进程的互斥与同步问题,如果涉及在同一个类中有多个静态方法中处理多线程共享数据的话,那就变成用信号量解决生产者-消费者问题。也就是说,静态方法是一份临界资源,对静态方法的访问属于进入临界区;对静态变量的修改是一份临界资源,对静态变量的修改属于进入临界区。


民和回族土族自治县15638291048: java中++i和i++的区别 -
屈幸黛力: 无论在java还是在其他语言中,++i和i++的用法都是一样的,主要差别体现在直接使用中: ++i是先对i进行自增操作然后再进行运算,如1 2inti = 10; System.out.println(++i); // 这时输出结果为11 i++是先进行运算然后再进行自增操作,如1 2...

民和回族土族自治县15638291048: java 中 i++和++i有什么区别 -
屈幸黛力: i++和++i都是对i进行+1的操作,不同的是i++是在i值被使用后才+1而++i是i值在被使用之前就+1;拿你的例子进行说明 先分析count=(i++)+(i++)+(i++);运算是从左向右的,由于i++是在i值被使用后才+1所以第一个括号的(i++)应该为3,但是i的...

民和回族土族自治县15638291048: java编程里++i怎么用 -
屈幸黛力: ++i 是先自增在使用 i++是先计算结果在自增 比如 int i=1 ,j=0; j=++i+1; 这个时候j=3 如果是i++的话 int i=1 ,j=0; j=i++ +1; 这个时候j=2

民和回族土族自治县15638291048: java中i++和++i的区别 -
屈幸黛力: i++是先使用i值,然后将i+1. ++i是先将i+1,然后使用i+1后的值.

民和回族土族自治县15638291048: java中( - i + i + i++)_
屈幸黛力: 后自增操作符即i++返回的是将操作数原来的、未修改的值作为表达式的结果值; 例如i=0; j=i++(此时i还没有自增); 执行到这里的时候 j=0,i=1 前自增操作符即++i返回的是将修改后的的操作数的值作为表达式的结果值. i=0; j=++i(i先进行自增...

民和回族土族自治县15638291048: java中的i++运算 -
屈幸黛力: 两次 public class MyClass {static int i=0;public int aMethod(){i++;return i;}public static void main(String args[]){MyClass test=new MyClass();test.aMethod(); // 一次int j=test.aMethod(); //第二次System.out.println(j);} }

民和回族土族自治县15638291048: java里的i++什么意思 -
屈幸黛力: i在原有值的基础上再加1,比如i原来的值是1,经过i++后,i的值就变为2了. 另一种写法 : i = i + 1

民和回族土族自治县15638291048: 在java里语言里关于I++的计算方式
屈幸黛力: 首先明确一个概念i++是先执行在计算,++i是先计算在执行. 那么首先i=i++;先执行在计算,所以i++的值还是0;那么就是i=0;而执行完之后i++才变成1,但是这里要注意,i并不是static静态的,所以他在内存中的数据是不会保存的,当i++赋值给i之后,i++的值就销毁了,所以在执行完i=i++;i 的值还是0. 那么第二次i=i++;还是先执行在计算如同上面,所以i的值还是0.两次输出都是0

民和回族土族自治县15638291048: java 中的 ++ 或者 _- 是二元操作符吗? -
屈幸黛力: 一元运算符 一些只需要一个操作数的运算符称为一元运算符(或单目运算符). 例:5 + 6 和 5++ 前者5 和 6为操作数 + 为运算符,但是 + 需要两个操作数,所以它不是一元运算符,而是二元运算符,而后者只需要一个操作数,所以 ++ 为一元运算符.

民和回族土族自治县15638291048: java中++i和i++的用法理解 -
屈幸黛力: 这个用for循环是测不出来的.. for循环的执行顺序是: (1)i = 1; (2)判断i <= 10; (3)执行循环体; (4)执行i ++或者 ++i; (5)跳到(2),直到判断条件不满足,,退出 不管你是先执行i++还是++i,在第(4)步执行完成之后i的值都已经自增了..测试方法: 直接使用System.out.println(i++); 和System.out.println(++i);就能够看出不同了..

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网