• 58、synchronized同步方法


    线程安全问题

    先看下面代码出现的问题:

    定义一个Task类,里面有一个成员变量和一个有boolean类型参数的方法,方法内部会根据传入参数修改成员变量的值。

    package com.sutaoyu.Thread;
    
    public class Task {
        //成员变量存储在堆内存里面,多个线程访问同一个堆内存,
        //即多个线程可以同时修改num的值,这样会导致线程安全问题
        private int num = 0;
        public void changeNum(boolean flag) {
            if(flag) {
                num = 88;
                System.out.println(Thread.currentThread().getName() + "======" + "begin");
                System.out.println(Thread.currentThread().getName() + "=====" + num);
                System.out.println(Thread.currentThread().getName() + "=====" + "over")
            }else {
                System.out.println(Thread.currentThread().getName() + "=====" + "begin");
                System.out.println(Thread.currentThread().getName() + "=====" + num);
                System.out.println(Thread.currentThread().getName() + "=====" + "over");
            }
        }
    }

    创建一个Task对象,将这个对象放到两个线程中,在这两个线程中分别调用changeNum方法

    package com.sutaoyu.Thread;
    
    public class SynchronizedTest01 {
        public static void main(String[] args) {
            Task task = new Task();
            Thread t1 = new Thread() {
                public void run() {
                    task.changeNum(true);
                }
            };
            Thread t2 = new Thread() {
                public void run(){
                    task.changeNum(false);
                }
            };
            
            t1.start();
            t2.start();
        }
    }

    上面的代码有可能会出现打印这样的结果:

    Thread-1=====begin
    Thread-0=====begin
    Thread-1=====66
    Thread-0=====66
    Thread-1=====over
    Thread-0=====over

    正常情况下应该打印出一个88一个66,可是上面却两个线程打印出的两个66,这样就出现了线程安全的问题,出现这个问题的原因是成员变量存储在堆内存中,两个线程共享堆内存,即两个线程可以对同一个num进行修改。
    程序执行分析:
    cpu执行t1线程,将num修改为88,之后cpu开始执行t2线程,将num修改为66,打印出66,cpu开始执行t1线程,打印num的值,此时num的值是66。

    内存图解:

    同步和异步

    比如你要给A,B,C三人发消息
    同步:先给A发,等A回复后,再给B发,等B回复后,再给C发,排队等待
    异步:直接给A,B,C发消息,中间不需要等某人回复之后再给其他人发消息,不用排队等待

    使用synchronized将方法设置为同步

    将Task中的changeNum方法设置为同步的

    public synchronized void changeNum(boolean flag)

    在方法上加入synchronized关键字,这样在执行多个线程时看哪个线程先执行这个方法,假设有t1,t2,t3三个线程中都调用了changeNum方法,t1线程先执行了这个方法,那么t1会先在Task对象上面加锁,加锁后,别的线程就无法执行当前Task对象上的changeNum方法,直到t1执行结束changeNum方法之后,t2,t3中的一个线程才可以执行这个方法,这就保证了在某个时间段内只有一个线程执行changeNum方法,解决了线程安全问题。
    注意:synchronized锁住的是当前对象,如果t1线程和t2线程里面是不同的对象,则不需要同步,因为不会发生线程安全问题。如下代码:

    package com.sutaoyu.Thread;
    
    public class SynchronizedTest01 {
        public static void main(String[] args) {
            
            Task task1 = new Task();
            Task task2 = new Task();
            
            Thread t1 = new Thread() {
                public void run() {
                    task1.changeNum(true);
                }
            };
            Thread t2 = new Thread() {
                public void run(){
                    task2.changeNum(false);
                }
            };
            
            t1.start();
            t2.start();
        }
    }
  • 相关阅读:
    关于工作中Git相关的总结
    浅谈MySQL的优化
    由内搜推送思考Kafka 的原理
    SOA和微服务架构
    Centos7.2 搭建Lamp服务器以及迁移WordPress个人博客详细过程
    MyISAM和InnoDB索引实现区别
    图解高内聚与低耦合
    图解Java常用数据结构(一)
    Java源码安全审查
    Java高并发之锁优化
  • 原文地址:https://www.cnblogs.com/zhuifeng-mayi/p/10155532.html
Copyright © 2020-2023  润新知