package atomic; public class JoinThread extends Thread { public static int i = 0; //public static AtomicInteger atomicInteger = new AtomicInteger(0); public synchronized void inc(){ i ++; } @Override public void run() { for (int x = 0; x < 10; x++) { inc(); try { Thread.sleep(33); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub // JoinThread jt = new JoinThread(); Thread[] t = new Thread[100]; for (int i = 0; i < t.length; i++) { t[i] = new <span style="font-family: Arial, Helvetica, sans-serif;">JoinThread</span>(); } for (int i = 0; i < t.length; i++) { t[i].start(); } for (int i = 0; i < t.length; i++) { t[i].join(); } System.out.println(JoinThread.i); } }
执行完发现,i并无如想像中的输出
1000
,即便i添加
volatile
进行修饰,也不会输出
1000
,值是随机变化的。
java
将inc()方法添加static修饰,结果无问题,准确无误的输出1000。设计模式
另一种改法,将代码改为:多线程
Thread[] t = new Thread[100]; for (int i = 0; i < t.length; i++) { t[i] = new JoinThread(); }
修改为:并发
JoinThread jt = new JoinThread(); Thread[] t = new Thread[100]; for (int i = 0; i < t.length; i++) { t[i] = new Thread(jt); }
结果无问题,准确无误的输出1000jvm
这里主要涉及到类对象(static方法),对象方法(非static方法)ide
咱们知道,当synchronized修饰一个static方法时,多线程下,获取的是类锁(即Class自己,注意:不是实例);atom
当synchronized修饰一个非static方法时,多线程下,获取的是对象锁(即类的实例对象)spa
因此,当synchronized修饰一个static方法时,建立线程无论是new JoinThread()仍是new Thread(new JoinThread()),在run方法中执行inc()方法都是同步的;线程
相反,当synchronized修饰一个非static方法时,若是用new JoinThread()仍是new Thread(new JoinThread())方式建立线程,就没法保证同步操做,由于这时设计
inc()是属于对象方法,每一个线程都执有一个独立的对象实例new JoinThread(),因此多线程下执行inc()方法并不会产生互斥,也不会有同步操做。
另外若是考虑到变动的原子操做,可以使用atomic包下面的包装对象,这些对象都是对volatile修饰变量的一种延伸,可保证变量的原子操做而不用去同步方法或
代码块是否同步。
一个日本做者-结成浩的《java多线程设计模式》有这样的一个列子:
pulbic class Something(){
public synchronized void isSyncA(){}
public synchronized void isSyncB(){}
public static synchronized void cSyncA(){}
public static synchronized void cSyncB(){}
}
那么,加入有Something类的两个实例a与b,那么下列组方法何以被1个以上线程同时访问呢
a. x.isSyncA()与x.isSyncB()
b. x.isSyncA()与y.isSyncA()
c. x.cSyncA()与y.cSyncB()
d. x.isSyncA()与Something.cSyncA()
这里,很清楚的能够判断:
a,都是对同一个实例的synchronized域访问,所以不能被同时访问
b,是针对不一样实例的,所以能够同时被访问
c,由于是static synchronized,因此不一样实例之间仍然会被限制,至关于Something.isSyncA()与 Something.isSyncB()了,所以不能被同时访问。
那么,第d呢?,书上的 答案是能够被同时访问的,答案理由是synchronzied的是实例方法与synchronzied的类方法因为锁定(lock)不一样的缘由。
synchronized是对类的当前实例进行加锁,防止其余线程同时访问该类的该实例的全部synchronized块,注意这里是“类的当前实例”, 类的两个不一样实例就没有这种约束了。那么static synchronized刚好就是要控制类的全部实例的访问了,static synchronized是限制线程同时访问jvm中该类的全部实例同时访问对应的代码快。实际上,在类中某方法或某代码块中有 synchronized,那么在生成一个该类实例后,改类也就有一个监视快,放置线程并发访问改实例synchronized保护快,而static synchronized则是全部该类的实例公用一个监视快了,也也就是两个的区别了