Java 详细讲解线程安全与同步附实例与注释

 更新时间:2022年04月14日 10:11:05   作者:汤键.TJ  
线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况

线程安全问题

多个线程可能会共享(访问)同一个资源

比如访问同一个对象,同一个变量,同一个文件

当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题,称为线程安全问题

什么情况下会出现线程安全问题

多个线程共享同一个资源

且至少有一个线程正在执行写的操作

实例:

存钱取钱问题

分别有存钱和取钱2个线程

   存钱                      取钱
   线程1         余额         线程2
   1000  《----1000------》 1000
   1000+1000-----》2000
                500 《-----1000-500

正确:结束后余额应该是1500,而不是500

买票问题

有卖票2个线程

   卖票                      卖票
   线程1         票数         线程2
   1000  《----1000------》 1000
   1000-1-----》999
                999 《-----1000-1

正确:结束后余额应该是998,而不是999

买票问题错误(未线程同步)实例:

public class love implements Runnable{
    private int piao=3000;//有3000张票
    public boolean sale() {//ture代表还有票;false代表没有票了
        if(piao<1) return false;
         piao--;//卖1张票
         
         //细化piao--;
         //寄存器=piao;
         //寄存器=寄存器-1;
         //piao=寄存器;
         
         String sk =Thread.currentThread().getName();//获取当前线程(买票窗口)的名字
         System.out.println(sk+"卖了1张票,还剩下"+piao+"张");
         return piao>1;
    }
    public void run() {
         while(sale());//循环执行;直至卖完票返回false
    }
}
 
public class Main {
    public static void main(String[] a) {
        love tjlove =new love();
        for(int i=1;i<=4;i++) {//循环4次;产生4个线程(窗口)卖票
            Thread tj = new Thread(tjlove());
            tj.setName(""+i);
            tj.start();
        }
    }
}

部分输出结果:

线程安全问题

分析问题

线程A和B对类中1个变量值为17进行+1操作
最终结果为2个18

解决方案

加锁:

过程:首先线程A先访问到这个17,读上来后进行加锁并进去+1的操作改为18
并且17在加锁期间其它线程都不能访问
改完之后再进行写入,然后再解锁17
然后再由线程B去访问它,再进行加锁,重复上面操作变成19再解锁
这样做能保证在同一时间只有1个线程去访问它,这样就保证了安全;之前错误是由于这些线程一起去访问了它

线程同步

刚刚所说的加锁操作便是线程同步技术

可以使用线程同步技术来解决线程安全问题

线程同步在Java里有2种做法:

1.同步语句

2.同步方法

同步语句

public class love implements Runnable{
	private int piao=3000;//本人cpu单核性能过强,数据量大些才能看到是4个线程在卖票
	public boolean sale() {
		synchronized(this) {//1个线程获取这个对象的锁,并加锁;    synchronized作用于整个语句
		//this指向当前对象
		//不能用new Object();这样会产生新的对象,产生新的锁
		//把this换成"123",效果基本一样;因为其存在常量值里,每次访问的对象一样
			if(piao<1) return false;
			piao--;
			String sk =Thread.currentThread().getName();
			System.out.println(sk+"卖了1张票,还剩下"+piao+"张");
			return piao>0;
			}
	}
	public void run() {
		 while(sale());
	}
}

部分输出结果:

synchronize(obj)的原理

1.每个对象都有一个与它相关的内部锁(intrinsic lock)或者叫监视器锁(monitor lock)

2.第一个执行到同步语句的线程可以获得 obj 的内部锁,在执行完同步语句中的代码后释放此锁

3.只要一个线程持有了内部锁,那么其它线程在同一时刻将无法再获得此锁

✓ 当它们试图获取此锁时,将会进入BLOCKED状态

4.多个线程访问同一个 synchronized(obj)语句时

obj必须是同一个对象,才能起到同步的作用

同步方法

public class love implements Runnable{
    private int piao=3000;
    public synchronized boolean sale() { //synchronized作用于整个方法
            if(piao<1) return false;
            piao--;
            String sk =Thread.currentThread().getName();
            System.out.println(sk+"卖了1张票,还剩下"+piao+"张");
            return piao>0;
    }
    public void run() {
         while(sale());
    }
}

synchronized不能修饰构造方法

同步方法的本质

实例方法:synchronized (this)

静态方法:synchronized (Class对象)

同步语句比同步方法更灵活一点

同步语句可以精确控制需要加锁的代码范围,减少处于BLOCKED状态的线程,充分利用劳动力

使用了线程同步技术后

虽然解决了线程安全问题,但是降低了程序的执行效率

因为加了锁就会有处于等待的线程,多了加锁解锁的操作

所以在真正有必要的时候,才使用线程同步技术

到此这篇关于Java 详细讲解线程安全与同步附实例与注释的文章就介绍到这了,更多相关Java 线程安全内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java测试题 实现一个注册功能过程解析

    Java测试题 实现一个注册功能过程解析

    这篇文章主要介绍了Java测试题 实现一个注册功能过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • 使用Vue+Spring Boot实现Excel上传功能

    使用Vue+Spring Boot实现Excel上传功能

    这篇文章主要介绍了使用Vue+Spring Boot实现Excel上传,需要的朋友可以参考下
    2018-11-11
  • 基于Java8 函数式接口理解及测试

    基于Java8 函数式接口理解及测试

    下面小编就为大家带来一篇基于Java8 函数式接口理解及测试。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Java语言之包和继承详解

    Java语言之包和继承详解

    这篇文章主要介绍了java的包和继承,结合实例形式详细分析了Java继承的概念、原理、用法及相关操作注意事项,需要的朋友可以参考下
    2021-09-09
  • springboot的EnvironmentPostProcessor接口方法源码解析

    springboot的EnvironmentPostProcessor接口方法源码解析

    这篇文章主要介绍了springboot的EnvironmentPostProcessor接口方法源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • springboot整合mybatis中的问题及出现的一些问题小结

    springboot整合mybatis中的问题及出现的一些问题小结

    这篇文章主要介绍了springboot整合mybatis中的问题及出现的一些问题小结,本文给出了解决方案,需要的朋友可以参考下
    2018-11-11
  • java 将方法作为传参--多态的实例

    java 将方法作为传参--多态的实例

    下面小编就为大家带来一篇java 将方法作为传参--多态的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • java String类功能、原理与应用案例【统计、判断、转换等】

    java String类功能、原理与应用案例【统计、判断、转换等】

    这篇文章主要介绍了java String类功能、原理与应用案例,结合实例形式详细分析了java String类的基本功能、构造方法,以及使用String类实现统计、判断、转换等功能相关操作技巧,需要的朋友可以参考下
    2019-03-03
  • Java基础之反射

    Java基础之反射

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;反射是框架设计的灵魂,感兴趣的小伙伴可以参考阅读
    2023-03-03
  • 优化常见的java排序算法

    优化常见的java排序算法

    这篇文章主要介绍了Java编程中快速排序算法的实现及相关算法优化,快速排序算法的最差时间复杂度为(n^2),最优时间复杂度为(n\log n),存在优化的空间,需要的朋友可以参考下
    2021-07-07

最新评论