大家有没有想过一个问题,Java中的++
操作是不是线程安全的呢,也就是说在多线程情况下,多个线程一起去执行++
操作,得到的结果会不会是我们所预期的结果呢,可以写个demo去验证一下。
public static int count = 0; public static void main(String[] args) { for (int j = 0; j < 10000; j++) { new Thread(new Runnable() { @Override public void run() { add(); } }).start(); } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count); } public static void add(){ count++; }
最后的执行结果如下图:
很明显,Java中的++
操作,并不是线程安全的。
这就涉及到了多线程环境下,如何才能保证在操作同一个变量或者方法时保证线程安全。
Synchronized关键字,就是利用一个特定的对象设置一个锁(lock),在多线程并发访问的时候,只允许一个线程可以获得这个锁,并执行代码,等到代码执行完毕后,释放锁,再继续由其他线程争抢。
我们可以稍微改造一下上面的demo,让++
操作变成线程安全的。
public static int count = 0; public static void main(String[] args) { for (int j = 0; j < 10000; j++) { new Thread(new Runnable() { @Override public void run() { add(); } }).start(); } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count); } public synchronized static void add(){ count++; }
只是做个一个很小的改动,就是在add()方法中加入synchronized关键字,看一下执行结果:
多次执行得到的结果都是10000,是符合我们预期的,**add()**方法也变成了一个线程安全的方法。
Synchronized有三种不同的使用场景,同时也对应着不同的锁对象。
将上面demo的**add()**方法改造一下
public static void add(){ Object o = new Object(); //锁对象 synchronized (o){ count++; } }
定义一个Object对象,将Object作为锁对象,只会有一个线程获得这个锁对象,执行++
操作,别的线程需要等待锁对象被释放时候,争抢这个锁对象,获得锁对象的才能继续执行++
。
public synchronized void add(){ ... ... }
在这个add()方法的锁对象又是什么呢,很明显,就是this当前对象,线程获得的是当先对象。
public synchronized static void add(){ count++; }
最后一种就是demo中的**add()**方法,这时的锁对象就是当前类的字节码对象,因为是静态方法,静态方法是属于这个类的,所以锁对象就是当前类的字节码对象。
Synchronized使用在代码块中,锁对象可以是任意对象。 Synchronized使用在方法中,锁对象为当前对象this。 Synchronized使用在静态方法中,锁对象为当前类的字节码对象。
Vector是线程安全的,ArrayList、LinkedList是线程不安全的
Properties是线程安全的,HashSet、TreeSet是不安全的
StringBuffer是线程安全的,StringBuilder是线程不安全的
HashTable是线程安全的,HashMap是线程不安全的
Synchronized虽然可以保证多线程共享数据的数据安全,但是如果使用不当,线程长期持有锁对象,未及时得到释放,很容易造成死锁问题,
而且在性能问题上,Synchronized对性能也会有一定的影响,所以请慎用Synchronized。
感谢观看,如有帮助请点个赞支持一下,谢谢。