前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >10. 线程安全问题 / synchronized 关键字(重点)

10. 线程安全问题 / synchronized 关键字(重点)

作者头像
小雨的分享社区
发布2022-10-26 15:28:52
2110
发布2022-10-26 15:28:52
举报
文章被收录于专栏:小雨的CSDN

1. 线程不安全

线程不安全代码:

代码语言:javascript
复制
public class ThreadDemo13 {
    static class Counter{
        public static int count = 0;

        public void increase(){
            count++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread t1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };

        Thread t2 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };

        t1.start();
        t2.start();

        t1.join();//当执行这里的时候,线程就阻塞了,一直到t1结束,才会往下执行
        t2.join();

        System.out.println(Counter.count);
    }
}

以上代码有两个新的线程,此时最终的运行结果不唯一

2. 为什么会出现线程不安全的情况呢?

1.线程是抢占式执行的(根本原因)

2.自增操作不是原子的,每次++,都能分为以下三个步骤 a)把内存中的数据读取到CPU (load) b)把CPU中的数据+1 (incr) c)把计算结束的数据写回到内存 (save) 当CPU执行到任意一步骤的时候,调度器随时都有可能调度走,来让其他线程来执行

3.多个线程尝试修改同一个变量

4.内存可见性导致的线程安全问题

5.指令重排序(在编译器编译代码时,会对指针进行优化,调整指令的先后顺序,保证原有逻辑不变的情况下提高程序的运行效率)

3. 加锁 synchronized

1)实现了原子性 性能比较低

加上 synchronized 之后不一定立即就能成功,如果发现当前的锁已经被占用,该代码就会阻塞等待,一直等到之前的线程释放,才可能会获取到这个锁

以下代码假设线程一先获取到锁,那么线程二再尝试获取锁就会阻塞等待,线程一的运行不会受到影响。当线程一释放锁之后,线程二才有可能获取到锁。如果此时线程一中出现死锁,一旦锁死,就解不开了

代码语言:javascript
复制
public class ThreadDemo13 {
    static class Counter{
        public static int count = 0;

        //表示进入此方法之前会尝试加锁
        //increase方法执行完毕后会自动解锁
  
        //这里就相当于是针对counter这个对象进行加锁
        //进入方法内部,把加锁状态设为true,执行完成这个方法之后,就把加锁状态设为false
        synchronized public void increase(){
            count++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread t1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };

        Thread t2 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };

        t1.start();
        t2.start();

        t1.join();//当执行这里的时候,线程就阻塞了,一直到t1结束,才会往下执行
        t2.join();

        System.out.println(Counter.count);
    }
}

此时运行结果就唯一了

2)以下是加锁和非加锁的对照: StringBuffer(加锁了) / StringBuilder(没加锁) Vector(加锁了) / ArrayList(没加锁) HashTable (加锁了)/ HashMap(没加锁)

3)理解synchronized具体使用 synchronized是可以灵活加锁的

  1. 加到普通方法前:表示锁this 如果synchronized关键字写到方法前面,那就相当于是给当前对象来加锁
  1. 加到静态方法前:表示锁当前类的类对象——反射
  2. 加到某一代码块之前:显示指定给某个对象加锁
代码语言:javascript
复制
public class ThreadDemo14 {
    static class Test{
        public void method(){
            //这里括号为this,就相当于是给当前创建的对象加锁(t)
            synchronized (this){
                System.out.println("hh");
            }
        }
    }

    public static void main(String[] args) {
        Test t = new Test();
        t.method();
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-02-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 线程不安全
  • 2. 为什么会出现线程不安全的情况呢?
  • 3. 加锁 synchronized
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档