线程与并发 - synchronized

多线程与高并发
  • synchronized 当多个线程同时访问同一个资源的时候需要对这个资源上锁。
  • synchronized 既保证原子性,也保证线程间的可见性

当我们对一个数字进行递增操作时,如果两个程序同时访问,第一个线程读到count=0,并对其+1,在自己线程内部的内存里还没有写回去的时候;第二个线程读到的count也是0,并+1写回去;但是程序明明对count进行了两次+1操作,但结果还是1。
那么我们对这个递增过程加上一把锁,当第一个程序访问的时候,这个资源是它独占的,不允许别的线程访问计算,当第一个线程计算完成并释放锁之后其它线程才能访问,这样就保证了线程安全。

public class Thread_006 {
    private static int count = 0;

    public static void main(String[] args) {
        new Thread(()->{
            countAdd();
        }).start();

        new Thread(()->{
            countAdd();
        }).start();
    }
	//去掉synchronized 可以看出来不加锁的情况下我们预期的结果与实际结果是不符合的
    static /*synchronized*/ void countAdd(){
        for (int i = 0; i < 100; i++) {
            try {
                //相当于你线程处理的业务
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
        }
        System.out.println(Thread.currentThread().getName()+"count = "+ count);
    }
}
当不在countAdd方法上加锁时的执行结果:
Thread-1count = 155
Thread-0count = 155

当在countAdd方法上加锁的执行结果:
Thread-0count = 100
Thread-1count = 200
synchronized 加锁的方式
  • 我们加锁代码有个原则:尽量少的锁代码,比如我的方法中只需要对count++进行synchronized,我们没必要把整个方法锁住,只锁count++就可以了。但是如果我们一个方法内有N多个地方需要加锁,我们就直接锁方法,就不要每次都让线程竞争了。
public class Thread_007 {

    private int count = 0;
    private Object o = new Object();//不可以使用String常量 Integer Long

    //锁定Object
    public void m1(){
        synchronized (o){
            count++;
        }
    }

    //锁定当前对象
    public void m2(){
        synchronized (this){
            count++;
        }
    }

    //等同与m2锁定当前对象
    public synchronized void m3(){
        count++;
    }
}
静态方法加锁
  • 静态方法是没有this对象的,我们也可以不用new出来一个对象进行加锁
public class Thread_008 {
    private static int count = 10;

    //等同于synchronized (Thread_008.class)
    public synchronized static void m1(){
        count--;
    }

    public static void m2(){
        synchronized (Thread_008.class){
            count--;
        }
    }
}
synchronized保证原子性与线程间可见
public class Thread_009 {
    private int count  = 100000;

    public /*synchronized*/ void run(){
        count--;
        System.out.println(Thread.currentThread().getName()+" count = "+count);
    }

    public static void main(String[] args) {
        Thread_009 t = new Thread_009();
        for (int i = 100000; i > 0 ; i--) {
            new Thread(t::run,"Thread "+i+" ").start();
        }
    }
}
不加sychronized执行结果,我们可以看到多个线程输出了同一个值,最终结果不是我们预期的0
......
Thread 38223  count = 38230
Thread 38166  count = 38229
Thread 38226  count = 38229
Thread 38169  count = 38229
Thread 38284  count = 38230
Thread 38168  count = 38228
......
当我们加上sychronized时,会解决这个问题
Thread 4  count = 3
Thread 5  count = 2
Thread 2  count = 1
Thread 1  count = 0

面试题:模拟银行账户,对业务写方法加锁,对业务读方法不加锁,可以吗?
答:不可以,容易产生脏读现象;具体看代码,解决方法就是把读方法也加锁

public class Thread_010 {
    String name;
    double balance;

    public /*synchronized*/ void set(String name,double balance){
        this.name = name;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.balance = balance;
    }

    public synchronized double getBalance(){
        return this.balance;
    }

    public static void main(String[] args) {
        Thread_010 account = new Thread_010();
        new Thread(()->account.set("柯南",300.0)).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("当前账户余额:"+account.getBalance());

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("当前账户余额:"+account.getBalance());
    }
}
不加锁执行结果:
当前账户余额:0.0
当前账户余额:300.0
加锁执行结果:
当前账户余额:300.0
当前账户余额:300.0
synchronized 锁重入|异常锁
  • 锁重入:一个同步方法内可以调用另一个同步方法,及一个类中两个方法都加了锁,那么他们锁定的都是同一个对象,当我们在m1中调用m2时,会发现他们呢是同一个线程的申请这把锁,允许执行m2,这就叫锁重入。
  • 异常锁:程序执行过程中,如果某个环节出现异常,默认锁会被释放,外部等待的程序就会冲进来,程序乱入,可能会访问到异常时产生的数据;一般用try-catch解决,保证流程是不会被异常中断的。
synchronized 锁升级

面试题:CAS(自旋锁)一定比系统锁的效率高吗?
答:不一定,分具体情况:执行时间短(加锁的代码),线程数少,用自旋;执行时间长,线程数多,用系统锁。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值