多线程之synchronized使用

本文详细介绍了Java中synchronized的使用,包括线程安全与非线程安全的概念、方法内的变量线程安全、实例变量非线程安全及其解决方案、多个对象与多个锁、脏读问题与解决、锁重入特性、异常时锁的自动释放以及同步不具有继承性。通过示例代码展示了synchronized如何确保线程安全,并避免脏读等问题。
摘要由CSDN通过智能技术生成

之前几个文章中我已经说明了”线程安全“和”非线程安全“。
”非线程安全“其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是”脏读“,也就是取到的数据其实是被更改过得。而”线程安全“就是以获得的实例变量的值是进过同步处理的,不会出现藏独的现象。

1.方法内的变量为线程安全

”非线程安全“问题存在于”实例变量“中,如果是方法内部的私有变量,则不存在”非线程安全“问题,所得结果也就是”线程安全“的了。
下面我以一段代码说明,在实现方法内部声明一个变量时,是不存在”非线程安全“问题得。
示例代码:
方法代码:

package test;

/**
 * @Author LiBinquan
 */
public class HasSelfPrivateNum {
   
    public void addNum(String userName){
   
        try{
   
            int num = 0;
            if ("a".equals(userName)){
   
                num = 100;
                System.out.println("a set over");
                Thread.sleep(1000);
            }else{
   
               num = 200;
                System.out.println("b set over");
            }
            System.out.println(userName +" num = "+num);
        }catch (InterruptedException e){
   
            e.printStackTrace();
        }
    }
}

线程1:

package test;

import java.util.Random;

/**
 * @Author LiBinquan
 */
public class ThreadTest1 extends Thread{
   
    private HasSelfPrivateNum name;
    public ThreadTest1(HasSelfPrivateNum name){
   
        super();
        this.name = name;
    }

    @Override
    public void run() {
   
        super.run();
        name.addNum("a");
    }
}

线程2:

package test;

import java.util.Random;

/**
 * @Author LiBinquan
 */
public class ThreadTest2 extends Thread{
   
    private HasSelfPrivateNum name;
    public ThreadTest2(HasSelfPrivateNum name){
   
        super();
        this.name = name;
    }

    @Override
    public void run() {
   
        super.run();
        name.addNum("b");
    }
}

运行类代码:

package test;

/**
 * @Author LiBinquan
 */
public class Run {
   
    public static void main(String[] args) throws InterruptedException {
   
        HasSelfPrivateNum num = new HasSelfPrivateNum();
        ThreadTest1 threadTest1 = new ThreadTest1(num);
        threadTest1.start();
        ThreadTest2 threadTest2 = new ThreadTest2(num);
        threadTest2.start();
    }
}

输出:
在这里插入图片描述

由输出可见,方法中的变量不存在非线程安全问题,永远都是线程安全的。这是方法内部的变量时私有的特性造成的。

2.实例变量非线程安全

如果多个线程共同访问1个对象中的实例变量,则有可能出现”非线程安全“问题。
用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉情况。
我们将HasSelfPrivateNum类中的方法修改一下,
代码如下:

package test;

/**
 * @Author LiBinquan
 */
public class HasSelfPrivateNum {
   
    private int num =0;
    public void addNum(String userName){
   
        try{
   
            
            if ("a".equals(userName)){
   
                num = 100;
                System.out.println("a set over");
                Thread.sleep(1000);
            }else{
   
               num = 200;
                System.out.println("b set over");
            }
            System.out.println(userName +" num = "+num);
        }catch (InterruptedException e){
   
            e.printStackTrace();
        }
    }
}

输出结果:
在这里插入图片描述
由输出的内容我们可以看出,如果两个线程同时操作业务对象中的实例变量,则有可能出现”非线程安全“问题。这边我们只需要将HasSelfPrivateNum类中的方法加上synchronized即可。
代码如下:

package test;

/**
 * @Author LiBinquan
 */
public class HasSelfPrivateNum {
   
    private int num =0;
    synchronized public void addNum(String userName){
   
        try{
   

            if ("a".equals(userName)){
   
                num = 100;
                System.out.println("a set over");
                Thread.sleep(1000);
            }else{
   
               num = 200;
                System.out.println("b set over");
            }
            System.out.println(userName +" num = "+num);
        }catch (InterruptedException e){
   
            e.printStackTrace();
        }
    }
}

输出:
在这里插入图片描述
结论:在两个线程范文同一个对象中的同步方法时一定是线程安全的。

3.多个对象多个锁

这个我们还是以上面的测试代码为例,我们只需要将输出类改为:
代码如下:

package test;

/**
 * @Author LiBinquan
 */
public class Run {
   
    public static void main(String[] args) throws InterruptedException {
   
        HasSelfPrivateNum num1 = new HasSelfPrivateNum();
        HasSelfPrivateNum num2 = new HasSelfPrivateNum();
        ThreadTest1 threadTest1 = new ThreadTest1(num1);
        threadTest1.start();
        ThreadTest2 threadTest2 = new ThreadTest2(num2);
        threadTest2.start();
    }
}

输出:
在这里插入图片描述
由上输出可得出,两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是以一部的方式运行的。这个测试用例中创建了2个业务对象,在系统中产生出2个锁,所以运行结果是异步的,打印的效果就是先打印b,然后打印a。
从上面程序运行结果看,虽然在HasSelfPrivateNum中使用了synchronized关键字,但打印的顺序确实不同步的,是交叉的。
从这边可以看出synchronized取得的锁都是对

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值