1. 什么是原子操作:
原子操作就是计算机在执行指令过程中不可分割的最小指令单元, 不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行倒结束,中间不会有任何 context switch (切换到另一个线程)。 比如声明变量的操作、非long和double型的primitive进行赋值,以及返回这两者之外的primitive,这些都是原子操作,这些操作是安全的,注意++, --操作不是原子操作;有些文章中强调:在多线程的程序中,一旦将某个关键代码封装成一个原子操作,那么对它们的操作就不会存在不同步的情况。个人不太认可这种看法,看如下代码:
public class Counter{ private int num; public int getNum(){ return this.num; } public void setNum(int num){ this.num = num; } }
这里的setter方法只有赋值操作,是原子操作,假设有如下的情景:num的初始值为0,线程A调用setNum(5)方法中,然后线程B调用setNum(10),然后线程A调用getNum得到的值是5而不是10,这是因为线程A得到的是是从其私有内存中(什么是私有内存,请看第2点)返回的;解决问题的方法:(1)将num的声明改为private volatile int num; (2)在getter和setter方法前加关键字synchronized.
后记:似乎这种情景不会存在,因为一般是通过Counter的实例 去调用setNum方法,如果一个线程更改了num值,则是通过Counter的实例进行更改的,只要两个线程共享的是同一个Counter,那么结果还是同步的,所以不需要volatile和 synchronized, 看如下的代码:
public class MultiThreadCounterTest { public static void main(String...strings){ final Counter counter = new Counter(); new Thread(){ public void run() { counter.setNum(5); try { sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(counter.getNum()); } }.start(); new Thread(){ public void run() { counter.setNum(10); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } }
打印出的结果是10;
2.什么是私有内存,什么是共享内存( 下面是个人的理解 ):
根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。
每条线程都有自己的工作内存(Working Memory),也就是私有内存,此工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。
3.为什么++,--不是原子操作( 最好通过发编译过程看执行步骤)
以n++为例,操作方式是先取出n的值,然后对其加1,再赋给n,这里牵涉到读和写两步操作,所以不是原子操作;
4.同步方法锁定的是什么对象:
(1)实例方法使用的锁是当前的对象,当线程进入当前对象的方法并获取当前对象为锁,其他的前程就不能在获取到此对象锁,执行对象的任意一个同步方法;如下代码:
public synchronized void doSomething(){ //... }
等同于:
public void doSomething(){ synchronized(this){ //... } }
(2)静态方法使用的锁 是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了),如:
Class Test{ public synchronized static void methodAAA(){ //… } }
和如下代码相同的作用,两者获取相同的锁:
Class Test{ public static void methodAAA(){ synchronized(Test.class){ //... } } }