synchronized是用来控制线程同步的,在多线程的情况下保证代码不被同时执行。synchronized的使用情况有下面几种
1、修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
2、修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
3、修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
在不用synchronized的情况下看如下代码:
private static int number = 0; class Demo implements Runnable{ @Override public void run() { for(int j=0;j<200000;j++){ add(); } } private void add(){ number++; } }
Demo demo = new Demo(); Thread thread = new Thread(demo); Thread thread2 = new Thread(demo); Thread thread3 = new Thread(demo); thread.start(); thread2.start(); thread3.start(); try { thread.join(); thread2.join(); thread3.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程结束number:"+number);
结果如下:
并不是预期的600000.由于++该操作是先读取值,然后写回一个新值,相当于原来的值加上1,分两步完成,如果第二个线程在第一个线程读取旧值和写回新值期间读取i的域值,那么第二个线程就会与第一个线程一起看到同一个值,并执行相同值的加1操作,这也就造成了线程安全失败。加上synchronized,代码修改如下:
class Demo implements Runnable{ @Override public void run() { for(int j=0;j<200000;j++){ add(); } } private synchronized void add(){ number++; } }
再次运行结果如下:
上面的synchronized是在方法上面的,属于第一种当前实例加锁,如果不用同一个对象,new多个对象结果会怎样?看如下代码
Thread thread = new Thread(new Demo()); Thread thread2 = new Thread(new Demo()); Thread thread3 = new Thread(new Demo()); thread.start(); thread2.start(); thread3.start(); try { thread.join(); thread2.join(); thread3.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程结束number:"+number);
结果如下:
同样结果不是600000.因为synchronized修饰方法,是对该对象的锁,新建不同对象的时候就会出现同时运行加的方法
下面我们来看看第二种,修饰静态方法:
代码修改如下:
Thread thread = new Thread(new Demo()); Thread thread2 = new Thread(new Demo()); Thread thread3 = new Thread(new Demo()); thread.start(); thread2.start(); thread3.start(); try { thread.join(); thread2.join(); thread3.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程结束number:"+number); } private static int number = 0; class Demo implements Runnable{ @Override public void run() { for(int j=0;j<200000;j++){ add(); } } } private static synchronized void add(){ number++; }
结果如下:
synchronized 修饰静态方法,其实就是对类的加锁,所以结果也是600000,同理,在代码块中加上synchronized (xxx.class)也是对类的加锁,同时synchronized (Object o)就是对对象o的锁,这两种就是用于代码块的使用。另外synchronized (this)是对该类的实例对象的加锁。其实也不复杂,自己调试一下就清楚了,下面有张图片,便于记忆