初学java,对线程安全及并发这块不了解,有些问题需要请教 10C

在看到关于synchronized用法的一个例子时,有些疑惑

 package com.company;

public class TicketDB {
    // 机票的数量
    private int ticketCount = 1;
    // 获得当前机票数量
    public  int getTicketCount() {
        return ticketCount;
    }
    // 销售机票
    public  void sellTicket() {
        try {
            // 等于用户付款
            // 线程休眠,阻塞当前线程,模拟等待用户付款
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        System.out.printf("第%d号票,已经售出\n", ticketCount);
        ticketCount--;
    }
}

package com.company;

public class HelloWorld {
    public static void main(String[] args)
    {
        TicketDB db=new TicketDB();
        Thread t1 = new Thread(() ->
        {
            while (true) {
                int currTicketCount = db.getTicketCount();
                // 查询是否有票
                if (currTicketCount > 0) {
                    db.sellTicket();
                }
                else {
                    // 无票退出
                    break;
                }
            }
        });
        // 开始线程t1
        t1.start();
        // 创建线程t2
        Thread t2 = new Thread(() -> {
            while (true) {
                int currTicketCount = db.getTicketCount();
                // 查询是否有票
                if (currTicketCount > 0) {
                    db.sellTicket();
                } else {
                    // 无票退出
                    break;
                }
            }
        });
        // 开始线程t2
        t2.start();
    }
}

输出结果如下:
第1号票,已经售出
第1号票,已经售出

这个结果我能有所理解,但后续该书中说到:将类TicketDB中的getTicketCount和sellTicket方法前加入synchronized关键字,可以防止这种重复卖票的现象。将类TicketDB进行修改,如下:
package com.company;

public class TicketDB {
    // 机票的数量
    private int ticketCount = 1;
    // 获得当前机票数量
    public  synchronized int getTicketCount() {
        return ticketCount;
    }
    // 销售机票
    public  synchronized void sellTicket() {
        try {
            // 等于用户付款
            // 线程休眠,阻塞当前线程,模拟等待用户付款
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        System.out.printf("第%d号票,已经售出\n", ticketCount);
        ticketCount--;
    }
}
结果确实有变:
第1号票,已经售出

Q1:为何会这样?有没有可能这样,第一个线程执行完int currTicketCount = db. tTicketCount() ;此时该线程对应的工作内存中的currTicketCount值为1,然后改线程退出运行状态进入就绪状态,此时第二个线程得到CPU资源进入运行状态,也执行intcurrTicketCount=db.tTicketCount(),那此时由于线程1未执行sellTicket,那么线程2中的currTicketCount值也为1,那么假设此时线程1又进入running状态,当它售完票后,票数为0,但由于线程2的currTicketCount为1,满足大于0的条件,那么线程2也会进行sellTicket操作,那么是否会出现第0张票已经售出的奇怪现象

Q2:运行时,电脑是多核的,用jvisualvm看线程,发现这两个线程始终在时间上是错开的,无法并行运算,为什么?多核的也无法让多线程程序并行吗?
0

3个回答

synchronized是可重入锁,其实你所说的情况就是因为synchronized是可重入锁的原因,你可以去了解一下可重入锁和不可重入锁就知道了。

简单说就是,加了synchronized的方法调用加了synchronized的方法,他们会使用同一把“锁”,所以你说的那种情况也就不会出现了,因为 第二个线程那时候去拿“锁”的时候,会拿不到,自然也就进不去方法了

0
weixin_40229805
weixin_40229805 回复weixin_38652136: 我了解了下synchronized,第一个线程执行getTicketCount时,第二个线程无法访问该方法,但当第一个方法执行完毕后,锁是被释放的,假设分配给该线程的时间片用完了,该线程进入runnable状态,此时该线程未执行sellTicket这个synchronized方法,那么第二个线程是有可能在期间执行getTicketCount方法,此时第二个线程的工作内存中的currTicketCount也是1,因为还没有哪个线程执行过sellTicket,这样当某一线程a执行sellTicket时,另一个线程b无法执行sellTicket,当a执行完后,它把ticketCount写回到主内存中,此时ticketCount为0,线程b执行的时候,不就有可能出现第0张票出售的情况吗?刚学,许多不明白,说错了还请指教
8 个月之前 回复
weixin_40229805
weixin_40229805 回复weixin_38652136: 我了解了下synchronized,第一个线程执行getTicketCount时,第二个线程无法访问该方法,但当第一个方法执行完毕后,而且还未执行第二个syn
8 个月之前 回复
weixin_38652136
邪恶八进制 多核能让线程并发,但是有锁的存在,锁相当于cpu指针,只有就像你切换电脑应用程序一样,只有谁拿到了cpu指针谁才能运行,不然就只能在后台跑
8 个月之前 回复
weixin_38652136
邪恶八进制 你说的那种情况根本不存在的,因为sleep是休眠,不会释放锁的,第二个线程根本拿不到锁,所以不可能进去,wait才会释放锁
8 个月之前 回复
weixin_38652136
邪恶八进制 回复weixin_40229805:重复提交是什么并发情况,加了synchronized后加了锁,也就是说同一时间,只能有一个线程进入方法内部执行业务逻辑,当然也就不会存在 第0张票出售的情况啦
8 个月之前 回复
weixin_40229805
weixin_40229805 这两个方法之间没看到有调用啊?
8 个月之前 回复

浅解:如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法。所以getTicketCount和sellTicket每次只能执行一个,所以不存在同时getTicketCount和sellTicket

0

给普通方法加synchronized 默认用的是this 这个锁
因为两个线程都是用的db这个对象加锁 所以不存在你说的那种情况

-3
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
其他相关推荐
关于java中多线程的问题的一点思考(什么情况下需要考虑到线程,以及线程安全的问题?)
参与java开发已经有一年左右,算是一个初级的程序员,对于项目中线程的问题有一些自己的看法。同时对于线程而言,是一个比较复杂的问题,在编程中需要着重考虑的地方就是线程的问题。代码中的线程安全问题也是需要通过大量的编程实践经验来考虑的。      对于java中线程的基础部分在这里暂时不讨论,主要了解下java线程的使用场景以及需要重点考虑线程安全的地方有哪些? 第一点:无状态的对象一定是线程安
[AOP] 5. Spring AOP中提供的种种Aspects - 并发控制
本文继续讨论ConcurrencyThrottleInterceptor(基于Spring 4.3.7)。以及上一篇文章中遗留的一个关于SimpleAsyncTaskExecutor类中属性concurrencyLimit的问题。这些都和并发控制相关。但是这里需要事先说明的一点是,这些类和实现的年代都比较久远了,比如ConcurrencyThrottleInterceptor是在2004年的Spri
Java并发(1) i++的线程安全问题
1. 问题背景条件 多线程操作同一变量 此处不可能是局部变量,肯定是成员变量或静态变量 PS:为什么不可能是局部变量? 需要多线程访问局部变量,那么多个线程就必须在方法中进行定义,如使用匿名内部类方式定义多个线程。 在使用匿名内部类引用局部变量时,局部变量必须通过final来修饰。这样做的原因是变量的生命周期问题。 final修饰的变量,不管是int还是Integer,都不能进行自加操作。 2.
高并发下System.currentTimeMillis()并发问题以及优化对比
前言 在高并发场景下System.currentTimeMillis()并发问题严重,甚至比创建一个普通对象要耗时的多;在系统中有时候不可避免要打印一些时间戳,但怎么做才更好呢。 代码实现 iimport java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java...
【Java】关于Java8 parallelStream并发安全的思考
【Java】关于Java8 parallelStream并发安全的思考 背景 Java8的stream接口极大地减少了for循环写法的复杂性,stream提供了map/reduce/collect等一系列聚合接口,还支持并发操作:parallelStream。 在爬虫开发过程中,经常会遇到遍历一个很大的集合做重复的操作,这时候如果使用串行执行会相当耗时,因此一般会采用多线程来提速。Java8...
从构造函数看线程安全
线程是编程中常用而且强大的手段,需要面对的就是线程安全问题。Java 中的构造函数是否是线程安全的呢?
java并发之如何解决线程安全问题
× 资讯 安全 ...
Java Web并发访问的线程安全问题
多线程环境下如果访问单例对象,当对象内部有类变量或实例变量时,就可能存在安全性问题。 解决方法: 1.对操作共享变量的所用方法进行同步控制; 2.同步共享变量,例如Collections.synchronizedMap()可以同步共享的Map。 3.使用同步对象,例如ConcurrentMap、AtomicInteger等对象都是线程安全的,使用AtomicInteger可以统计系统的并发量。
java MD5 多线程环境下共享锁还是每次创建一个新的MessageDigest
    在多线程环境下对字符串进行MD5,到底应该使用加锁来共享同一个MessageDigest呢?还是每次新创建一个,个人认为需要 根据程序运行的环境来分别对待。下边是从org.springframework.extensions.surf摘取的一段代码,实现了两种调用方式, 不过到底在何种情况下使用何种方式,目前还不是很清晰,希望通过测试能够得出结论。 import java.secur...
JAVA中如何保证线程安全以及主键自增有序
一、常见场景 多个线程针对一个i进行主键自增。多线程下如果不做安全策略,将会导致各个现成获取的i值重复,导致脏数据 常见策略 1、增加syschroize进行线程同步 2、使用lock、unlock处理 3、使用reetrantent 锁进行锁定 缺点:容易造成性能低下,或者编写代码容易造成死锁 二、新方案 jdk新提供的功能,atomicInteger
享元设计模式 -- 线程的不安全性
这篇博客主要分析了下享元模式的线程不安全行,因为网上没找到相关的博客, 享元模式的学习参考: http://www.cnblogs.com/chenssy/p/3330555.html http://www.cnblogs.com/rush/archive/2011/10/01/2197785.html今天看了下享元的设计模式, 概念如下: 所谓享元模式就是运行共享技术有效地支持大量细粒
System.out.println 的多线程并发问题
如果println函数的参数为常量则不会出现线程并发问题,但是如果参数为表达式形式,则JVM在执行println函数的时候会分为几步来执行,从而造成并发问题。
MessageDigest 使用注意,并发问题
说一下最近在开发过程中遇到加密相关的问题,先引用一段MD5的解释。百度百科 MD5 Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。该算法的文件号为RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Securi
java Vector 在多线程使用中需要注意的问题
众所周知,Java中有一些被称为是线程安全的集合容器,但是这里的线程安全会让人误以为在多线程环境中去使用这些容器就可以放心使用,包打天下。但是事实上并不是如此,在多线程中使用这些类仍然会存在问题。这就让人迷茫了,明明是线程安全的,为什么还会出错呢。我的理解是,明明是线程安全的,但是这些集合并不一定能在多线程环境中不出问题。 先看一段测试代码:public class VectorTest { pri
java模拟并发请求测试方法是否线程安全
java模拟并发请求测试方法是否线程安全
第十章并发与分布式编程——并发性与线程安全
并发与分布式编程(Concurrent and Distributed Programming)1.并发(Concurrency)注意并发并不等于平行(Parallel),下面的图片就形象的解释了这一概念:在现代编程中,并发性是必不可少的:多用户并发请求服务App在手机端与云端都有计算GUI的前端用户操作和后台的计算并发编程主要有两种模式:共享内存:在内存中读写共享数据。eg:两个处理器共享内存、...
SpringMVC和Struts2并发访问时的线程安全问题
SpringMVC是基于方法的拦截,Struts2是基于类的拦截。 Struts2每处理一个请求,就会实例化一个Action对象,所有不会有线程安全的问题。 SpringMVC的controller默认是singleton的。 单例的好处: 不用每次创建controller 减少创建对象的时间和垃圾收集的时间。 意味着每一个请求,系统都会用原有实例去处理,这样会导致多线程调用时,它里面的实例变
JDK 中的并发集合
非原创文章,转自:http://novoland.github.io/%E5%B9%B6%E5%8F%91/2014/07/26/%E5%B9%B6%E5%8F%91%E9%9B%86%E5%90%88.html JDK 中的并发集合 JDK 中的并发集合1. Collections 类提供的并发集合2. JUC 提供的并发集合 Con
Java并发(一)-- 线程安全(从“线程不安全”说起)
来聊一聊Java并发中的“线程安全”问题
java8 lambda的并发处理
Java8的发布已经有4个月了,网上关于java8的特性的文章也有很多。其中最引人注意的当然是lambda表达式了。可惜很多文章都是浅尝辄止,或者都是复制粘贴。这里就lambda一个方面说说自己的想法。 1、有一篇文章说,java8开创了lambda表达式。这真的是误人子弟。 大家可以参考这本书:《计算机程序的构造和解释》,里面有关于lambda表达式的详细说明。在函数式编程领域,...
局部变量一定线程安全吗
局部变量, 如果是基本类型或是包装类型, 依然不能通过多线程改变其值, 如果是对象, 则其属性值是线程不安全的 demo1: public void compute(LocalDate date){ //在切换租户时,只有当前线程的第一个租户id设置有效,或者说一个线程内无法切换数据源 TenantContext.set("saas_base"); ...
关于Java OutputStream 线程安全问题
今天偶尔发现java的输出流的线程安全问题 先看代码吧 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.OutputStream; import java.util.Random; import java.util.concurrent.Ti
一篇文章看懂Java并发和线程安全(一)
一、前言     长久以来,一直想剖析一下Java线程安全的本质,但是苦于有些微观的点想不明白,便搁置了下来,前段时间慢慢想明白了,便把所有的点串联起来,趁着思路清晰,整理成这样一篇文章。 二、导读     1、为什么有多线程?     2、线程安全描述的本...
关于Base64工具类并发问题
BASE64 编码是一种常用的字符编码,在很多地方都会用到。JDK 中提供了非常方便的 BASE64Encoder 和 BASE64Decoder,用它们可以非常方便的完成基于 BASE64 的编码和解码。很多时候为减少对象创建次数,一般会做如下编码: package com.
Connection实例是线程安全的吗
关于JDBC中关于Connection的两个疑问:   1.Connection实例是线程安全的吗?     即一个connection实例,在多线程环境中是否可以确保数据操作是安全的? Java代码   private static Connection connection;       上述代码,设计会不会有问题? 一个Connection
Static工具类线程安全问题
[b]1.静态方法[/b] 无论是静态方法还是实例方法,在内存中都只有一份代码,也就是只占用一份内存空间 方法属于一个程序块,只有当别人调用它时才会调到内存里面去执行,也就是说当前有多少个线程在执行就有多少组方法块里的局部变量 [b]2.静态变量[/b] 只存在一份,多个线程公用一份,一个线程修改就会影响其他线程 [b]3.结论[/b] 静态方法是使用得当...
java多线程高并发线程安全问题
在抢购之类的业务处理时,会出现超发之类的线程安全问题。我个人想到的解决方法如下:采用数据库锁,悲观锁有效率低下问题,所以我推荐乐观锁,虽然会增大CPU开销,很多服务和软件都支持乐观锁,如Redis的watch采用FIFO队列,强行把多线程变成单线程,但是也会出现队列内存爆满问题采用同步代码块,只给数据库操作的代码加锁,提高效率,对不同数据记录操作采用不同的锁...
Ehcache对并发的支持
Ehcache对并发的支持          在高并发的情况下,使用Ehcache缓存时,由于并发的读与写,我们读的数据有可能是错误的,我们写的数据也有可能意外的被覆盖。所幸的是Ehcache为我们提供了针对于缓存元素Key的Read(读)、Write(写)锁。当一个线程获取了某一Key的Read锁之后,其它线程获取针对于同一个Key的Read锁不会受到限制,但其它线程(包括获取了该Key的R
Java并发编程1 —— 线程安全问题是如何产生的
前提 在研究线程安全问题之前,有必要简单回顾一下jvm内存模型。 如下图所示,jvm内存模型可分成两大部分,即主存区和jvm内存。 主存区又叫做共享内存,顾名思义,是各个线程运行时所共享的内存区域,用来存放类加载时产生的对象实例,以及共享变量、静态变量、常量等。jvm内存为线程私有,即每个线程独享该内存,用来存放局部变量、方法信息等。 当线程需要访问共享变量时,首先需要从共享内存中读取...
Java并发编程:线程安全性
前言“共享”意味着变量可以由多个线程同时访问,而“可变”则意味着变量的值在其生命周期内可以发生变化。一个对象是否需要是线程安全,取决于它是否被多个线程访问。这指的是在程序中访问对象的方式,而不是对象要实现的功能。要使得对象是线程安全的,需要采用同步机制来协同对象可变状态的访问。如果无法实现协同,那么可能会导致数据破坏以及其他不该出现的结果。正题在开始编写文章前,有几个问题需要思考一下:什么是线程安...
java多线程并发去调用一个类的静态方法安全性探讨
这篇文章主要讲多线程对静态方法访问的数据安全性 总结如下: 1,java在执行静态方法时,会在内存中拷贝一份,如果静态方法所在的类里面没有静态的变量,那么线程访问就是安全的,比如在javaee中服务器必然会多线程的处理请求此时如果设计全局需要调用的静态方法,可用此种设计。 2,java在执行静态方法时,如果使用静态变量,同事类的设计时使用到了静态数据,最好在调用函数时使用synchr
Spring 是如何解决并发访问的线程安全性问题的
springmvc的controller是singleton的(非线程安全的),这也许就是他和struts2的区别吧!和Struts一样,Spring的Controller默认是Singleton的,这意味着每个request过来,系统都会用原有的instance去处理,这样导致了两个结果:一是我们不用每次创建Controller,二是减少了对象创建和垃圾收集的时间;由于只有一个Controlle
Java多线程 - 不要同步Boolean常量
在JAVA中通过synchronized语句可以实现多线程并发。使用同步代码块,JVM保证同一时间只有一个线程可以拥有某一对象的锁。锁机制实现了多个线程安全地对临界资源进行访问。   同步代码写法如下:   代码1: Object obj = new Object(); ... synchronized(obj) { //TODO: 访问临界资源 }     JAVA...
如何判断 Java 线程并发的安全性
前言 在高并发的时代中,如何写出高质量的并发程序一直是一个令人头疼的事情。现在给你一段代码,你如何判断它是否是线程安全的?又如何改进呢?在这里,我们简单介绍一下Java内部是如果保证线程安全的。一切的关键就在: 高效利用并发,同时也必须保证JMM三大特性的有序性。只有保证了有序性,才能在代码正确执行的前提下去求追更高的效率。而这个王牌就是:先行发生原则 想一想,如果Jav
SSH高性能并发的注意事项(个人总结)
1、  应该尽量减小service事务代码块的大小,在事务中对记录进行修改时,会对相应表加锁,如:A事务对table1 和table2表的记录进行修改,那么B事务再对table1 和table2表的记录进行修改时会等A事务commit以后才能获得table1和table2的锁,这样事务太长并发量大的时候系统就会变慢 2、  事务里对表的更新操作应该按一定顺序,如果顺序不同则可能会出现数据库表死锁
tomcat并发线程问题体会
最近一直在解决线上一个问题,表现是:Tomcat每到凌晨会有一个高峰,峰值的并发达到了3000以上,最后的结果是Tomcat线程池满了,日志看很多请求超过了1s。服务器性能很好,Tomcat版本是7.0.54,配置如下: <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="3000...
HttpClient多线程并发问题
这篇文章概括了怎样在多线程环境下安全的使用HttpClient。 建立连接 在HttpClient中使用多线程的一个主要原因是可以一次执行多个方法。在执行期间,每一个方法都使用一个HttpConnection实例。由于在同一时间多个连接只能安全地用于单一线程和方法和有限的资源,我们就必须确保连接分配给正确的方法。而MultiThreadedHttpConnectionManager完全可以
Java并发:线程安全与锁优化
概述人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情。有时候,良好的设计原则不得不向现实做出一些让步,我们必须让程序在计算机中正确无误地运行,然后再考虑如何将代码组织得更好,让程序运行更快。对于“高效并发”来说,首先需要保证并发的正确性,然后在此基础上实现高效。1.线程安全《Java Co...
HashMap多线程并发情况(JDK1.8)
HashMap多线程并发情况(JDK1.8) 很早就知道Hashmap是线程不安全的,并且也看过hashmap的源码,知道他的各个操作的过程,今天来实践下,在多线程的情况下,hashmap的哪些步骤会出问题。 撤销:Ctrl/Command + Z 重做:Ctrl/Command + Y 加粗:Ctrl/Command + Shift + B 斜体:Ctrl/Command + Shift + I...
Ehcache的并发支持
在高并发的情况下, 使用Ehcache缓存时, 由于并发的读与写, 我们读的数据有可能是错误的, 我们写的数据也有可能意外的被覆盖. 所幸的是Ehcache为我们提供了针对于缓存元素Key的Read(读)/Write(写)锁. Key的read锁可以同时被多个线程持有, 但要等到这些线程都释放掉read锁后, 其他线程才能获得write锁, 而且同一时间key的write锁只能被一个线程持有.
文章热词 机器学习教程 Objective-C培训 交互设计视频教程 颜色模型 设计制作学习
相关热词 mysql关联查询两次本表 native底部 react extjs glyph 图标 初学python请教学习路线 初学java的学习体会