synchronized的用法以及常见的使用错误

synchronized的作用是实现线程之间的同步,既能保证可见性,又能保证原子性。

用法也有很多种,如下所示


用一个线程类来演示下这三种:


指定加锁对象:

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这个对象可能一直在变,所以锁的对象一直在变,因此不能起到线程同步的作用,只要改成上面提到第一种用法的例子那样就可以解决。









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值