java基础知识回顾之java Thread类学习,二)--java多线程安全问题,锁)

技术java基础知识回顾之java Thread类学习,二)--java多线程安全问题,锁) java基础知识回顾之java Thread类学习(二)--java多线程安全问题(锁)上一节售票系统中我们

java线程类学习的java基础知识综述(二)——Java多线程安全问题(锁)

在票务系统的最后一节,我们发现打印出了错误的票0,-1,并且出现了多线程安全问题。我们分析多线程安全问题发生的原因。

看下面线程的主要代码:

@覆盖

public void run(){ 0

//TODO自动生成的方法存根

while(true){ 0

If(ticket 0){//调用线程0时,执行此判断语句时,调用线程1抢占CPU资源,线程0被冻结。

尝试{

thread . sleep(100);//中断当前活动的线程或正在执行的线程。

} catch(中断异常){ e

e . printstacktrace();

}

系统。out . println(thread . currentthread()。getname()“卖票”票-);

//system . out . println(thread . currentthread()。getId());

//system . out . println(thread . currentthread()。getName());

}

}

}

分析:的100张票卖到了最后一张,也就是当票=1的时候,有三个线程:Thread-0,Thread-1,Thread-2。此时,Thread-0进入的条件if(票据0)有资格执行,但它没有执行的权限。此时CPU切换到线程-1,线程-1也进入if(票证0)的状态。然后CPU切换到线程2,线程2再次进入阻塞状态。此时,所有三个线程都通过了if(票证0)判断,并且都将向下执行。此时,CPU资源被Thread-0的第一个线程抢占,执行ticket=1和ticket-2。然后第一个线程打印出票证=1,CPU被线程-2抓取,票证=0。同样,线程3在执行后打印出票证。

通过上面的分析大家知道了多线程安全产生的原因:的多个语句(if(ticket0)和ticket -)正在操作同一个线程的共享数据时(这里,共享数据是ticket=100),一个线程已经执行了多个语句的一部分,但是另一个线程抓取CPU资源并执行它们。导致数据共享错误。那么如何解决呢?

解决办法:的多个语句对共享数据进行操作时,只有一个线程可以完成执行,其他线程不能参与执行。对于java多线程安全,它提供了专业的解决方案,那就是锁。

已同步(对象){ 0

要同步的代码

}

这个“对象”叫做锁,持有锁的线程可以在同步代码块中执行,而没有锁的线程即使得到了CPU的执行权也进不去,因为它没有得到锁。

*火车上的厕所-经典”

火车售票的问题的解决:代码如下:

公共类TicketsRunnable实现了Runnable {

私人国际机票=100;

对象对象=新对象();//对象锁,由同步代码块使用

public ticket srunnable(){ 0

system . out . println(* * * * * * * * * * * * * * * * * * * *);

}

@覆盖

public void run(){ 0

//TODO自动生成的方法存根

while(true){ 0

同步(obj){ //同步代码块

If(ticket 0){//调用线程0时,执行此判断语句时,调用线程1抢占CPU资源,线程0被冻结。

尝试{

Thread.sleep(100);//中断当前活跃的线程,或者执行的线程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖票"+ticket--);
//System.out.println(Thread.currentThread().getId());
//System.out.println(Thread.currentThread().getName());
}
}

}
}
/**
* @param args
*/
public static void main(String[] args) {
TicketsRunnable runna = new TicketsRunnable();
Thread t1 = new Thread(runna);
Thread t2 = new Thread(runna);
Thread t3 = new Thread(runna);
t1.start();
t2.start();
t3.start();
}
}

通过测试,不会出现错票的问题。

分析代码:当火车票剩余1张的时候,这个时候假设Thread-0获取到了CPU的执行权,并且持有对象锁,进入if条件,打印出买票1。ticket--,这个时候即使其他的线程获取到CPU的执行资格,但是设Thread-0的锁还没有释放,其他的线程拿不到锁,这样就进入不了if条件,那么只要等Thread-0执行完,Thread-0执行完后,ticket=0,其他线程即使拿到锁,因为if(ticket 0)不能进入,所以执行不了。整个程序结束,卖票终止。

总结:通过程序可以知道:同步的前提是:1.必须要有两个或者两个以上的线程,才需要同步。

2.必须是多个线程使用同一个锁。

3.要分析哪段代码需要加同步,必须保证同步中只能有一个线程在运行。

同步锁的好处与弊端:1.好处,解决了多线程操作同意资源安全性问题。

2.弊端:多个线程每次都需要判断锁,较为消耗资源

这里举个例子讲解,同步synchronized在什么地方加,以及同步的前提:

*1.必须要有两个以上的线程,才需要同步。
* 2.必须是多个线程使用同一个锁。
* 3.必须保证同步中只能有一个线程在运行,锁加在哪一块代码

那么我们要思考的地方有:1.知道我们写的哪些是多线程代码

2.明确共享数据

3.明确多线程运行的代码中哪些语句是操作共享数据的。、

            4.要确保多个线程使用同一个锁。

下面的代码:需求:两个存户分别往银行存钱,每次村100块,分三次存完。

第一种写法:使用同步代码块的方式

class bank{
    private int sum;
    Object obj = new Object();//对象锁
    public  void add(int money){
        synchronized (obj) {
            sum +=money;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("sum="+sum);
        }
        
    }
    
}

第二种方法在函数上加synchronized:

class bank{
    private int sum;
    //函数封装代码(加synchronized)==同步块封装代码
    public synchronized void add(int money){
        sum +=money;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("sum="+sum);
    }
    
}
class Cus implements Runnable{
    private bank b = new bank();
    @Override
    public void run() {
        for(int i=0;i3;i++){
            //System.out.println(Thread.currentThread().getName());
            b.add(100);
        }
    }
}
public class BankDemo {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Cus c1 = new Cus();
        Thread t1 = new Thread(c1);
        Thread t2 = new Thread(c1);
        t1.start();
        t2.start();
    }
}

总结:同步应该加到add方法上面,因为add方法被run方法调用,所以是线程代码,这里的sum是共享数据,add方法 里面 sum +=money;操作共享数据。这里用了在普通方法上面加syncronized代替同步代码块,这也叫做同步函数。那么同步函数用的锁是什么我们下一节再接着讨论,多线程同步函数。

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/155860.html

(0)

相关推荐

  • 抖音点赞1.8元一单,抖音点赞技巧!

    技术抖音点赞1.8元一单,抖音点赞技巧!大家务必要可以客观的应对抖音短视频粉丝领域的收费标准价钱规范,并并不是贵的才算是好的自然也并不一定划算的价钱全是不靠谱的,只是要可以融合自身的要求选择专业靠谱的服务平台,而且必须可

    测评 2021年10月26日
  • hadoop是什么意思通俗易懂(hadoop 是什么东西)

    技术HADOOP概念是什么本篇内容主要讲解“HADOOP概念是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“HADOOP概念是什么”吧!大数据:指无法在一定时间范围内用常

    攻略 2021年12月22日
  • java开发MVC三层架构上再加一层Manager层原理的示例分析

    技术java开发MVC三层架构上再加一层Manager层原理的示例分析这篇文章主要为大家展示了“java开发MVC三层架构上再加一层Manager层原理的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面

    攻略 2021年10月31日
  • java的runtime执行命令的缺点(javaruntime参数设置)

    技术Java Runtime的使用方法是什么这篇文章将为大家详细讲解有关Java Runtime的使用方法是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。前言最近做项

    攻略 2021年12月15日
  • 有良田美池桑竹之属的属是什么意思,有良田美池桑竹之属的属的意思是

    技术有良田美池桑竹之属的属是什么意思,有良田美池桑竹之属的属的意思是解释:属:类有良田美池桑竹之属的属是什么意思。出自陶渊明的《桃花源记》原文:晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂

    生活 2021年10月23日
  • Hibernate持久化类怎么用

    技术Hibernate持久化类怎么用这篇文章主要介绍了Hibernate持久化类怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Hibernate持久

    攻略 2021年12月1日