synchronized的用法以及常见的使用错误
2017-06-23 20:15
302 查看
synchronized的作用是实现线程之间的同步,既能保证可见性,又能保证原子性。
用法也有很多种,如下所示
用一个线程类来演示下这三种:
指定加锁对象:
直接作用于实例方法:
直接作用于静态方法:
因为要体现静态方法,所以实例化了两个不同的AddThread对象。
新手使用synchronized的时候可能会犯下面的错误(不久前我也刚犯过)
1、
这两种都是对线程对象中的方法或局部变量进行加锁,但是又实例化了两个不同的Runnable实例,也就是说这两个线程使用的是两把不同的锁,因此不能保证线程安全,最后x的结果也会<=2000。
2、
第二种错误就比较隐晦
通过javap反编译run方法可以看到:
可以看到i++在执行的时候变成了 i=Integer.valueOf(i.intValue()+1);
Integer.valueOf():
他会倾向于返回一个代表指定数值的Integer实例。因此,i++的本质是,创建一个新的Integer对象,并将它的引用赋值给i。
这样的话,就可以知道了,因为i这个对象可能一直在变,所以锁的对象一直在变,因此不能起到线程同步的作用,只要改成上面提到第一种用法的例子那样就可以解决。
用法也有很多种,如下所示
用一个线程类来演示下这三种:
指定加锁对象:
package com.bckj.Thread; /** * Created by Admin on 2017/6/23. */ public class SynchronizedTest { static int x; static class AddThread implements Runnable{ Object o = new Object(); @Override public synchronized void run() { for(int i=1;i<=1000;i++){ synchronized (o){ x++; } } } } public static void main(String[] args) throws InterruptedException { Runnable run = new AddThread(); Thread addThread1 = new Thread(run); Thread addThread2 = new Thread(run); addThread1.start(); addThread2.start(); //让主线程等待上面两个线程执行完毕 addThread1.join(); addThread2.join(); System.out.println(x); } }
直接作用于实例方法:
package com.bckj.Thread; /** * Created by Admin on 2017/6/23. */ public class SynchronizedTest { static int x; static class AddThread implements Runnable{ public synchronized void increase(){ x++; } @Override public synchronized void run() { for(int i=1;i<=1000;i++){ increase(); } } } public static void main(String[] args) throws InterruptedException { Runnable run = new AddThread(); Thread addThread1 = new Thread(run); Thread addThread2 = new Thread(run); addThread1.start(); addThread2.start(); //让主线程等待上面两个线程执行完毕 addThread1.join(); addThread2.join(); System.out.println(x); } }
直接作用于静态方法:
package com.bckj.Thread; /** * Created by Admin on 2017/6/23. */ public class SynchronizedTest { static int x; static class AddThread implements Runnable{ public synchronized static void increase(){ x++; } @Override public synchronized void run() { for(int i=1;i<=1000;i++){ increase(); } } } public static void main(String[] args) throws InterruptedException { Runnable run = new AddThread(); Runnable run2 = new AddThread(); Thread addThread1 = new Thread(run); Thread addThread2 = new Thread(run2); addThread1.start(); addThread2.start(); //让主线程等待上面两个线程执行完毕 addThread1.join(); addThread2.join(); System.out.println(x); } }
因为要体现静态方法,所以实例化了两个不同的AddThread对象。
新手使用synchronized的时候可能会犯下面的错误(不久前我也刚犯过)
1、
public class SynchronizedTest { static int x; static class AddThread implements Runnable{ public synchronized void increase(){ x++; } @Override public void run() { for(int i=1;i<=1000;i++){ increase(); } } } public static void main(String[] args) throws InterruptedException { Runnable run = new AddThread(); Runnable run2 = new AddThread(); Thread addThread1 = new Thread(run); Thread addThread2 = new Thread(run2); addThread1.start(); addThread2.start(); //让主线程等待上面两个线程执行完毕 addThread1.join(); addThread2.join(); System.out.println(x); } }
public class SynchronizedTest { static int x; static class AddThread implements Runnable{ Object o = new Object(); @Override public void run() { for(int i=1;i<=1000;i++){ synchronized (o){ x++; } } } } public static void main(String[] args) throws InterruptedException { Thread addThread1 = new Thread(new AddThread()); Thread addThread2 = new Thread(new AddThread()); addThread1.start(); addThread2.start(); //让主线程等待上面两个线程执行完毕 addThread1.join(); addThread2.join(); System.out.println(x); } }
这两种都是对线程对象中的方法或局部变量进行加锁,但是又实例化了两个不同的Runnable实例,也就是说这两个线程使用的是两把不同的锁,因此不能保证线程安全,最后x的结果也会<=2000。
2、
第二种错误就比较隐晦
public class AddThread implements Runnable{ static Integer x = 0; @Override public void run() { for(int i=1;i<=1000;i++){ synchronized (x){ x++; } } } public static void main(String[] args) throws InterruptedException { Runnable run = new AddThread(); Thread addThread1 = new Thread(run); Thread addThread2 = new Thread(run); addThread1.start(); addThread2.start(); //让主线程等待上面两个线程执行完毕 addThread1.join(); addThread2.join(); System.out.println(x); } }这个程序看似没有错误,但是执行结果会得到比2000小的数值,主要是由于,Integer是不可变对象,对象一旦创建就不可能被修改。
通过javap反编译run方法可以看到:
可以看到i++在执行的时候变成了 i=Integer.valueOf(i.intValue()+1);
Integer.valueOf():
他会倾向于返回一个代表指定数值的Integer实例。因此,i++的本质是,创建一个新的Integer对象,并将它的引用赋值给i。
这样的话,就可以知道了,因为i这个对象可能一直在变,所以锁的对象一直在变,因此不能起到线程同步的作用,只要改成上面提到第一种用法的例子那样就可以解决。
相关文章推荐
- jquery全选/取消全选(反选)/单选操作以及获取值常见用法和错误-jquery prop()函数使用教程
- 使用IIS作为宿主发布WCF服务常见错误以及解决方法
- Android NDK开发(三)——常见错误集锦以及LOG使用
- 04-常见内存错误以及valgrind使用
- 04-常见内存错误以及valgrind使用
- iOS-cocoapods安装与使用以及常见错误
- C# 使用serialport 控件读写串口的大数据量解决方案 -- 以及与串口操作中常见的错误
- zxing的使用方法以及常见错误
- Android NDK开发(三)——常见错误集锦以及LOG使用,androidndk
- AspNetPager分页控件的使用以及常见错误
- Spring使用外部属性文件以及常见错误(内附大量图片,很有借鉴意义)
- iOS 中使用 XIB 自定义cell 的两种方法 以及 编译出现常见 的错误 ++++(xcode6.0之后)
- mahout中关联规则算法pfp-growth的使用以及常见错误
- 使用IIS作为宿主发布WCF服务常见错误以及解决方法
- AspNetPager分页控件的使用以及常见错误
- 使用交换函数交换两个变量的值(以int类型为例)的错误用法以及正确用法
- ASP.NET AJAX Tookit TabContainer 控件使用常见错误及解决方法
- windows 2008初体验常见问题: 无线网络"没有正确配置为使用IP协议" 错误解决办法
- 如何使用CCeButtonST v1.2控件,以及相应的wincore错误
- java常见错误以及可能原因