首页 > 其他分享 >synchronized介绍

synchronized介绍

时间:2022-12-20 12:45:35浏览次数:35  
标签:synchronized Thread void 介绍 t1 new public

synchronized

1、多线程之间容易出现线程安全问题

一个数由两个线程计算,一个线程加5000,另一个线程减5000,得出结果不为0

static int count = 0;
public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 5000; i++) {
            count++;
        }
    }, "t1");

    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 5000; i++) {
            count--;
        }
    }, "t2");

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

    System.out.println("结果:" + count);
}

    /**
     * 结果:-3826
     *
     * 进程已结束,退出代码为 0
     */

为什么要5000而不是+1、-1呢,我多次启动后发现每次结果都是0,怀疑是跟jvm热点数据有关,后续再来研究

1、原因分析

两个线程,线程1获取数据,加1操作;就在这时,加1操作完成之后,写入主内存之前,CPU时间片用完,发生上下文切换,线程2计算count,把结果写入主内存。线程1分配到CPU时间片,继续执行将count的值写入主内存,最后main线程得出来的结果不为0

2、临界区&临界资源

  • 临界区(Critical Section)

    • 一个程序运行多个线程本身是没有问题的
    • 问题出在多个线程访问共享资源
    • 多个线程共享资源其实也没有问题
    • 在多个线程对共享资源读写操作时发生指令交错,就会出现问题
    • 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
    • 每个进程中访问临界资源的那段程序称为临界区

  • 临界资源

    • 一次仅允许一个进程使用的共享资源

例如上面线程t1、t2对对类变量count进行写操作,改变count的值的那段for循环就是临界区,count就是临界资源

3、竟态条件

多个线程再临界区执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竟态条件

4、解决

为了避免临界区的竟态条件发生,有多种手段可以达到目的

  • 阻塞式解决方案:synchronized,Lock
  • 非阻塞式解决方案:原子变量

synchronized:对象锁,采用互斥的方式保证同一时刻只有一个线程能够持有对象锁, 其它线程再想获取对象锁时就会被阻塞住,这样就能保证拥有对象锁的线程能够安全的执行临界区的代码,不用担心线程上下文切换

Java中synchronized可以保证互斥和同步

  • ​ 互斥:同一时刻只能有一个线程执行临界区代码
  • ​ 同步:由于线程执行的先后顺序不同,需要一个线程等待其它线程运行到某个点

语法:

synchronized(object){ //线程1获取到锁,线程2阻塞

}

1、解决开头问题

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                synchronized (object){//线程1获取锁
                    count++;
                }
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                synchronized (object){//线程2获取锁
                    count--;
                }
            }
        }, "t2");

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

        System.out.println("结果:" + count);
    }

	/**
     * 结果:0
     */

synchronized理解

  • synchronized(object)中的object可以看作是房间,线程t1和t2都要进到房间内操作count
  • t1进入到房间,并锁住了门拿走了钥匙,t2只能在门外等待t1执行完synchronized{}内的代码
  • t1执行期间,并不是锁住了对象就能一直执行synchronized{}里的代码,CPU分配的时间片用完,还是要被踢出门外,但是门是锁住的,t1仍拿着钥匙。t1再次分配到时间片,就会被继续执行代码
  • t1执行完synchronized{}内的代码,会打开门,唤醒t2并把钥匙给t2。t2拿到钥匙,进入房间,锁住门,执行count--操作

synchronized实际是用对象锁保证了临界区内代码的原子性,临界区内的代码对外是不可分割的,不会被线程切换所打断。

2、锁对象面向对象改进

把锁对象放入一个类

public static void main(String[] args) throws InterruptedException {
        Room room = new Room();//使用对象,相当于原子类

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                room.increment();
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                room.decrement();
            }
        }, "t2");

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

        System.out.println("结果:" + room.getCount());
    }

class Room {
    int count;

    public void increment() {
        synchronized (this) {
            count++;
        }
    }

    public void decrement() {
        synchronized (this) {
            count--;
        }
    }

    public int getCount() {
        synchronized (this){
            return count;
        }
    }
}

结果还是0

3、加在方法上

相当于锁住了实例对象

    public void increment(){
        synchronized (this){
            
        }
    }

    //等价于
    public synchronized void increment(){
        
    }

相当于锁住了Class类对象(只有一个,实例对象可以有多个)

class Test{
    
    public static void increment(){
        synchronized (Test.class){

        }
    }

    //等价于
    public synchronized static void increment(){

    }
}

5、线程八锁

就是考察锁住了哪个对象

情况一:12(大概率)或者21

@Slf4j
public class Secure {

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

        Thread t1 = new Thread(() -> n.a());
        Thread t2 = new Thread(() -> n.b() );

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

@Slf4j
class Number{
    public synchronized void a(){//成员方法
        log.debug("1");
    }

    public synchronized void b(){//成员方法
        log.debug("2");
    }
}

情况二:1s 12或者2 1s 1

public class Secure {

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

        Thread t1 = new Thread(() -> {
			try {n1.a();} catch (InterruptedException e) {e.printStackTrace();}
        });
        Thread t2 = new Thread(() -> n.b() );

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

@Slf4j
class Number{
    public synchronized void a() throws InterruptedException {//成员方法
        Thread.sleep(1000);//睡眠1秒
        log.debug("1");
    }

    public synchronized void b(){//成员方法
        log.debug("2");
    }
}

情况三:3 1s 12 或 32 1s 1 或 23 1s 1

public class Secure {

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

        Thread t1 = new Thread(() -> {
			try {n1.a();} catch (InterruptedException e) {e.printStackTrace();}
        });
        Thread t2 = new Thread(() -> n.b() );
        Thread t3 = new Thread(() -> n.c() );

        t1.start();
        t2.start();
        t3.start();
    }
}

@Slf4j
class Number{
    public synchronized void a() throws InterruptedException {
        Thread.sleep(1000);//睡眠1秒
        log.debug("1");
    }

    public synchronized void b(){log.debug("2");}//成员方法

    public void c(){ log.debug("3"); }//成员方法
}

情况四:2 1s 1

public class Secure {

    public static void main(String[] args) throws InterruptedException {
        Number n1 = new Number();
        Number n2 = new Number();

        Thread t1 = new Thread(() -> {
			try {n1.a();} catch (InterruptedException e) {e.printStackTrace();}
        });
        Thread t2 = new Thread(() -> n2.b() );

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

@Slf4j
class Number{
    public synchronized void a() throws InterruptedException {//成员方法
        Thread.sleep(1000);//睡眠1秒
        log.debug("1");
    }

    public synchronized void b(){log.debug("2");}//成员方法
}

情况五:2 1s 1

public class Secure {

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

        Thread t1 = new Thread(() -> {
			try {n1.a();} catch (InterruptedException e) {e.printStackTrace();}
        });
        Thread t2 = new Thread(() -> n1.b() );

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

@Slf4j
class Number{
    public static synchronized void a() throws InterruptedException {//静态方法
        Thread.sleep(1000);//睡眠1秒
        log.debug("1");
    }

    public synchronized void b(){log.debug("2");}//成员方法
}

情况六:1s 12 或 2 1s 1

public class Secure {

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

        Thread t1 = new Thread(() -> {
            try {n1.a();} catch (InterruptedException e) {e.printStackTrace();}
        });
        Thread t2 = new Thread(() -> n1.b() );

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

@Slf4j
class Number{
    public static synchronized void a() throws InterruptedException {//静态方法
        Thread.sleep(1000);//睡眠1秒
        log.debug("1");
    }

    public static synchronized void b(){log.debug("2");}//静态方法
}

情况七:2 1s 1

public class Secure {

    public static void main(String[] args) throws InterruptedException {
        Number n1 = new Number();
        Number n2 = new Number();

        Thread t1 = new Thread(() -> {
            try {n1.a();} catch (InterruptedException e) {e.printStackTrace();}
        });
        Thread t2 = new Thread(() -> n2.b() );

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

@Slf4j
class Number{
    public static synchronized void a() throws InterruptedException {//静态方法
        Thread.sleep(1000);//睡眠1秒
        log.debug("1");
    }

    public synchronized void b(){log.debug("2");}//成员方法
}

情况八:1s 12 或 2 1s 1

public class Secure {

    public static void main(String[] args) throws InterruptedException {
        Number n1 = new Number();
        Number n2 = new Number();

        Thread t1 = new Thread(() -> {
            try {n1.a();} catch (InterruptedException e) {e.printStackTrace();}
        });
        Thread t2 = new Thread(() -> n2.b() );

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

@Slf4j
class Number{
    public static synchronized void a() throws InterruptedException {//静态方法
        Thread.sleep(1000);//睡眠1秒
        log.debug("1");
    }

    public static synchronized void b(){log.debug("2");}//静态方法
}

标签:synchronized,Thread,void,介绍,t1,new,public
From: https://www.cnblogs.com/WangJiQing/p/16993933.html

相关文章

  • JVS低代码基础介绍
    企业信息化底座    JVS是面向软件开发团队可以快速实现应用的基础开发脚手架,主要定位于企业信息化通用底座,采用微服务分布式框架,提供丰富的基础功能,集成众多业务引擎......
  • 界面控件DevExpress WPF中文指南 - 主题设计器工作区介绍
    DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专......
  • docker介绍及基本操作命令
    docker1.介绍#虚拟化虚拟化是一种资源管理技术,打破实体结构间的不可切割的障碍。虚拟化技术:VMware,VirtualBox,阿里云服务器,docker虚拟化技术#虚拟化相关名词 -......
  • 加粗表格的方法介绍
    方法1打开要操作的表格,选中表格。点击边框按钮边上的下拉箭头。 2点击线型。3点击粗线。 4点击边框按钮边上的下拉箭头,点击所有框线。5如图就将表......
  • [深度学习] CCPD车牌数据集介绍
    date:2021-06-0919:03:20+0800tag:-深度学习-图像处理-机器学习CCPD是一个大型的、多样化的、经过仔细标注的中国城市车牌开源数据集。CCPD数据......
  • django之Q查询进阶、ORM查询优化、ORM事务、ORM常用字段类型和字段参数、Ajax介绍、数
    一、Q查询进阶操作这里主要就是让查询数据的时候,可以使用input获取的信息,进行用户交互。fromdjango.db.modelsimportQq_obj=Q()#1.产生q对象q_obj.connector=......
  • Docker入门介绍
    1docker组件介绍#Docker:容器技术,资源隔离dotCloudgo写的软件Docker的基础是Linux容器(LXC)等技术LXC的基础上Docker进行了进一步的封装,让用户不......
  • [深度学习] CCPD车牌数据集介绍
    date:2021-06-0919:03:20+0800tag:-深度学习-图像处理-机器学习CCPD是一个大型的、多样化的、经过仔细标注的中国城市车牌开源数据集。CCPD数据......
  • Map中forEach方法介绍
    HashMap继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。AbstractMap实现了Map接口。简单例子:  Map接口里面有一个forEach方法(java8)  ......
  • 大数据专业介绍 | 北信科数据科学与大数据技术专业
    编者按你被复杂的专业名看花了眼吗?你还在看名字猜专业吗?那就和专业百科栏目一起,深入了解专业特色、行业背景、就业前景,更有专业教师、知名校友现身说法,为你深度解读专业的秘......