初学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张票出售的情况吗?刚学,许多不明白,说错了还请指教
10 个月之前 回复
weixin_40229805
weixin_40229805 回复weixin_38652136: 我了解了下synchronized,第一个线程执行getTicketCount时,第二个线程无法访问该方法,但当第一个方法执行完毕后,而且还未执行第二个syn
10 个月之前 回复
weixin_38652136
邪恶八进制 多核能让线程并发,但是有锁的存在,锁相当于cpu指针,只有就像你切换电脑应用程序一样,只有谁拿到了cpu指针谁才能运行,不然就只能在后台跑
10 个月之前 回复
weixin_38652136
邪恶八进制 你说的那种情况根本不存在的,因为sleep是休眠,不会释放锁的,第二个线程根本拿不到锁,所以不可能进去,wait才会释放锁
10 个月之前 回复
weixin_38652136
邪恶八进制 回复weixin_40229805:重复提交是什么并发情况,加了synchronized后加了锁,也就是说同一时间,只能有一个线程进入方法内部执行业务逻辑,当然也就不会存在 第0张票出售的情况啦
10 个月之前 回复
weixin_40229805
weixin_40229805 这两个方法之间没看到有调用啊?
10 个月之前 回复

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

0

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

-3
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
其他相关推荐
初学JAVA,有些问题搞不懂
我是初学JAVA,对JAVA不是很了解,希望大家能让我多了解一下JAVA。rn我有些问题不是很清楚:rn1.JAVA所编出的程序能生成可能执行文件吗?因为我觉得,用appletviwer或javac这样来运行JAVA实在是太麻烦了。rn2.JAVA可以开发底层一点的东西吗?如控制硬件方面的。rn3.什么是EJB?rn如果你们能给我再多一些了解,我万分感谢。
Java并发— 线程安全
1、什么是线程安全性    当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。       在线程安全类中封装了必要的同步机制,因此客户端无需进一步采取同步错失。 2、原子性 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的...
java并发的线程安全单例模式
转载请注明出处:jiq•钦'stechnical Blog 1、饿汉式 [java] view plain copy  print? public class Singleton {     private final static Singleton INSTANCE = new Singleton();     private S
Java并发——线程安全
线程安全主要是从以下五个方面来谈谈,如下图: 前奏—— 编写线程安全的代码,本质上就是管理对状态(state)的访问,而且通常都是共享的、可变的状态。 通俗地说,一个对象的状态就是它的数据,存储在状态变量(state variables)中,比如实例域或静态域.对象的状态还包括了其他附属对象的域。例如,HashMap的状态一部分存储到对象本身中,但同时也存储到很多Map.Entry对象中
Java并发(一)线程安全
什么是线程安全和非安全 如上图所示,所谓线程安全就是指在多个线程同时访问一个公共对象,不会因为多个线程并发读写,造成数据错误的情况。比如:同时启动100个线程,对一个list进行add 100个数据操作,对于非安全对象list在执行过程中,会有并发写的情况,造成数据丢失
Java并发编程实践——线程安全
编写线程安全的代码的核心在于,对对象状态访问的控制与管理,特别对共享的、可变的状态。   一般地讲,一个对象的状态就是它所包含的数据,存储在状态变量中,比如实例域或静态域。一个对象的状态可能还来自于它所依赖的其他对象,比如HashMap的状态一部分是存储在自己的对象空间之中的,但另一部分存储在许多的Map.Entry对象之间。所以一个对象的状态是指那些可被外界访问的方法所影响(改变)的数据。 ...
Java并发:可重入与线程安全
转载请注明出处:jiq•钦'stechnical Blog1函数的可重入性:函数可重入,意味着该函数可以同时由多个任务调用,而不会产生任何错误。可重入的函数不需要考虑线程安全问题,因为其不会引用任何共享数据。 可重入函数需满足几个条件:1、不能含有静态(全局)非常量数据2、不能返回静态(全局)非常量数据的地址。 3、只能处理由调用者提供的数据。4、不能依赖于单实例模式资源的锁。 5、不能调用不可重
[Java并发编程实战] 线程安全
本文介绍线程安全性相关的概念,包括原子性,竞态条件,复合操作,内置锁等,通过这些术语的介绍逐步铺开线程安全的相关知识,了解在哪些情况下应当用内置锁,哪些情况下用线程安全类就足够了。同时,说明应过多的同步会引起程序的活跃度和性能问题。 对于要编写线程安全的代码,核心在于对状态的访问操作进行管理,特别是对共享的和可变的状态的访问。 共享,表示可以由多个线程同时访问;可变,表示变量的值在其生...
java 并发线程安全 资料链接
深入剖析基于并发AQS的(独占锁)重入锁(ReetrantLock)及其Condition实现原理 http://blog.csdn.net/javazejian/article/details/75043422 剖析基于并发AQS的共享锁的实现(基于信号量Semaphore) http://blog.csdn.net/javazejian/article/details/7
【Java并发编程实践】— 线程安全
无论何时,只要有多于一个的线程访问给定的状态变量,而且其中某个线程会写入该变量,此时必须使用同步机制来协调线程对该变量的访问。Java提供了synchronized关键字,对具体一个对象实现线程独占,完成所谓的原子操作。 无论何时,只要有多于一个的线程访问给定的状态变量,而且其中某个线程会写入该变量,此时必须使用同步机制来协调线程对该变量的访
java 线程安全并发Queue
并发Queue        在并发的队列上jdk提供了两套实现,一个是以ConcurrentLinkedQueue为代表的高性能队列,一个是以BlockingQueue接口为代表的阻塞队列,无论在那种都继承自Queue。  如图继承Queue共有二十四个:  ConcurrentLinkedQueue 概念理解        ConcurrentLinkedQueue:是一个适用于高并发...
Java 并发之线程安全
写线程安全的代码,说白了就是管理一个类的共享的、可变的状态。只要有多于 1 个线程对类的状态进行写入,那么就必须用同步来协调这多个线程对状态的访问。对于一个没有状态的类来说(简单的理解就是只有方法没有成员变量,不储存值),它永远都是安全的。而对于有状态的类来说,就要保持其原子性来保证安全。   在多线程环境下,一种可能的风险就是 check-then-act (竞争条件的一种),就是 ch...
Java 并发 线程安全 ThreadLocal
java 并发 线程安全 ThreadLocal
Java并发:线程安全的单例模式
转载请注明出处:jiq•钦'stechnical Blog1、饿汉式public class Singleton {  private final static Singleton INSTANCE = new Singleton();  private Singleton() { }  public static Singleton getInstance() {     return INST
【Java并发】线程安全
什么是线程安全? 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用方代码不必作其他的协调,这个类的行为仍然是正确的,则称这个类是线程安全的类。   无状态的类是线程安全的 如果一个类是无状态的:不包含域也没有引用其他类的域。一次特定计算的瞬时状态会唯一地存在本地变量中,这些本地变量存储在线程的栈中,只有执行线程才能访问...
java并发之线程安全---java并发编程实践
2019独角兽企业重金招聘Python工程师标准>>> ...
【Java并发】Java内存模型和线程安全
Java内存模型1、原子性 指一个操作是不可中断的,即使实在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰。 i++是原子操作吗? 答案是否定的,两个线程对i++进行操作,线程1读到i++为2,线程2也进行操作读到i++为2,最终i=2,但是两个线程同时对i++进行操作,i应该为3的,所以i++并不是原子操作,不满足原子特性。2、有序性: 程序实际执行的顺序和你实际书写的顺序
java线程安全-java并发编程实践
2019独角兽企业重金招聘Python工程师标准>>> ...
并发及并发的线程安全处理
目录 线程安全性       原子性        提供了互斥访问,同一时刻只有一个线程可以来对它操作       原子包:具有原子性,线程安全的,        atomicInt:              源码实现unsafe类的getAndAddInt实现原理:循环判断当前的值和主内存值是否一致,相等就加一,用到的算                             ...
初学j2me,有些基本问题请教
问一下,要搭建一个环境,要装哪些东西,我已经装了j2sdk,midp2.0,wtk2.0,请问还要装什么,CLDC…………?我想有一个很方便的IDE,一个好点的工具来编写编译文件。
初学asm,有些简单问题请教大家:
1.我用的是8年以前在学校时用的教材书<>,书中讲的寄存器是AX,BX,CX之类,可是我在VC中看汇编,怎么全是eax,ecx,ebx了,是不是在前面加一个e就行了?rn2.还有大写小写有无区别?rn3.在VC中嵌入汇编是在asm前加一条下划线还是两条下划线?rn4.我正学到dos系统功能调用,书上讲要把中断号放入AH中,我直接放入ah中,运行的结果说是引用的oxFFFFFFFF的内存.我放入eah中,说是eah没定义,怎么回事?我的调用格式如下:rn __asmrn mov ah,1rn int 21hrn rnrn呵呵,暂时就这几个小问题了,请大侠们指点迷途
初学ASP.NET,有些问题请教大家
刚开始学,主要是看北大青岛的视频教程,碰到一个问题,就是新建一个用户控件,在其他页面引用这个用户控件的时候,这个用户控件的应用的样式失效了~!!rn[code=C#]rn rn[/code]rn默认的代码是这样的,貌似要把这个路径改成绝对路径rn[code=C#]rn rn[/code]rn就是根目录下Stylesheet文件夹下的“StyleSheet.css”!但改了以后,没效果,反而用户控件的样式都没了~!!rn还有个地方table的td设置背景,路径默认也是相对路径,改成绝对路径就不行了,背景就没掉了~!!!rn这是什么问题,这种情况要怎么解决?rn
初学jsp,有些问题请教一下
我编译了一个servlet:HelloWorldExample.class 书上说就放在WEB-INF的class目录下的,但我放了后仍不能访问,请问该放在哪里rnrnhttp://localhost:8080/mytest/servlet/HelloWorldExamplernrn错误:rnrnHTTP Status 404 - /mytest/servlet/HelloWorldExamplernrn--------------------------------------------------------------------------------rnrntype Status reportrnrnmessage /mytest/servlet/HelloWorldExamplernrndescription The requested resource (/mytest/servlet/HelloWorldExample) is not available.rnrnrn--------------------------------------------------------------------------------rnrnApache Tomcat/5.0.25rnrnrnjava文件为:rnrnimport java.io.*;rnimport java.text.*;rnimport java.util.*;rnimport javax.servlet.*;rnimport javax.servlet.http.*;rnrnpublic class HelloWorldExample extends HttpServletrn ResourceBundle rb=ResourceBundle.getBundle("LocalStrings");rn rn public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException,ServletExceptionrn response.setContentType("text/html");rn PrintWriter out=response.getWriter();rn rn out.println("");rn out.println("");rn String title=rb.getString("helloworld.title");rn out.println(" "+title+"");rn out.println("");rn out.println("");rn out.println("This is my servlet test");rn out.println("");rn out.println("");rn rn
自己初学php,有些问题请教
我下了个php expert editor请问要设置什么东西?我运行老是出错PHP PATH ERROR?rn还下了个wampserver,这个又要使用?
初学MIDlet,有些问题请教!tks!
本人一直都是从事.net开发的,rn但最近有股冲动,想学MIDletrn开发,因为没有涉足过java方rn面的开发,所以,想请教下,rn学习MIDlet,有像MSDN一样的rn帮助文档吗?rn如果有,能否提供提供链接地rn址或google搜索的关键字也行?rn本人找了很久都没找到。rn另外,还有什么好的资源网站rn吗?tks.
初学PHP,有些问题想请教
我刚刚涉及到PHP的开发,有写问题不是很明白rn1,我装的是ZEND 3.5.2plus,zend server好像是10.50的,但是安装的过程中提示server 和 optimizer不支持该版本的PHP,我的PHP是4.4.0,服务器是Apache1.3.3的rn2,不知道是什么问题,我下了一个论坛的源代码,里面有的代码是用zend加密了的,但是好像我的php不能解析加密过的代码,不知道是怎么回事rnrn请教大家了,谢谢。
初学表达式树,有些问题
1。expression> =(a,b)=>a/b;rn2。expression> =(a,b)=>a*b;rn3。expression> =(a,b,c)=>(a/b)*c;rn1,2怎么套起来变成3rn
初学VB,有些问题~
初学VB,有些不懂的rnstrConnection = "Provider=Microsoft.jet.oledb.4.0;Data Source=" & App.Path & "\db1.mdb"rncon.Open strConnectionrn这句话是什么意思?这个字符串Provider=Microsoft.jet.oledb.4.0代表什么?Data Source后面接的需要写出路径吗?rn初来,分不高,请见谅~
初学JAVA请教问题!!!
package mypackage;rnimport javax.swing.*;rnimport java.awt.event.*;rnrnpublic class Consolernrn //Creat a title string from the class namern public static String title(Object o)rn rn String t = o.getClass.toString(); //不能识别变量getClassrn //Remove the word "class"rn if(t.indexof("class") != -1) //不能识别indexof()方法rn t = t.substring(6);rn return t;rn rn public static void run(JFrame frame,int width,int height)rn rn frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);rn frame.setSize(width,height);rn frame.setVisible(true);rn rn public static void run(JFrame frame,int width,int height)rn rn frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);rn frame.setSize(width,height);rn frame.setVisible(true);rn rn public static void run(JApplet applet,int width,int height)rn rn JFrame frame = new JFrame(titile(applet));rn frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);rn frame.getContentPane().add(applet);rn frame.setSize(width,height);rn applet.init();rn applet.start();rn frame.setVisible(true);rn rn public static void run(JPanel panel,int width,int height)rn rn JFrame frame = new JFrame(title(panel)); //不能识别title()方法rn frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);rn frame.getContentPane().add(panel);rn frame.setSize(width,height);rn frame.setVisible(true);rn rnrnrn上面程序注释的地方总是出错误,为什么呀?我可是按照书本上写的!!
Java并发:线程安全的容器:同步和并发
转载请注明出处:1. 引言多线程环境正确发布共享数据的方法之一就是线程安全容器。线程安全的容器是由锁保护的域,将数据放入线程安全的容器中,可以保障其被安全地发布给所有从这个容器访问它的线程。2. 同步容器类JDK1.0开始有两个很老的同步容器类:Vector和HashTable。JDK1.2之后Collections工具类中添加了一些工厂方法返回类似的同步封装器类:public static C
03 Java 并发 线程安全理论基础
线程安全问题 线程安全问题可能是非常复杂的。 竞态条件 当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。 public class Counter { protected long count = 0; public void add(long value){
深入剖析java线程安全和并发
1.java从JDK1.0发布开始,使用的是HashTable和Vector等类提供支持“线程安全”操作的容器。但是,这一设计的不足之处是,对于每个使用它们的程序来说,不管是否对“线程安全”有需求,都要实现它,这样就降低了系统的性能 2.自JDK1.2级以后的java版本,用HashMap和ArrayList替换了HashTable和Vector。为了优化程序的性能,JDK提供了一个非同步的Co
Java并发——线程安全的集合(一)
1.线程安全的集合: 一般在并发情况下操作一些集合的时候,例如散列表,可能就会出现线程之间因为互相剥夺资源导致异常或死循环等情况。这个时候可以使用锁机制来保护共享的数据结构,但是如果使用线程安全的实现会更加的方便,之前讨论的阻塞队列就是线程安全的集合。 2.高效的映射、集和队列: java.util.concurrent包提供了映射、有序集和队列的高效实现:ConcurrentHashMap...
【Java并发】线程安全与锁优化
1 线程安全 个人定义:共享变量被多线程操作时,可被视作是以‘原子’形态被其他人‘可见’的‘有序’进行,且能够获取正确的操作结果,则它就是线程安全的。 1.1 安全等级 不可变:如final修饰变量在没有this逃逸情况下,变量本身是线程安全的,但变量内部的值还是可能被修改的 绝对线程安全:字面意思,绝对保证该变量的操作具有原子性有序性可见性 相对线程安全:对象提供的操作是安全的,但使用...
java线程安全之并发Queue(十三)
java线程安全之并发Queue(十三)
Java并发编程实战(一)线程安全
第一章 基础知识(线程安全) 线程安全的核心问题:处理对象状态的问题。如果要处理的对象是无状态的(不变性),或者可以避免多个线程共享的(线程封闭),那么我们可以放心,这个对象可能是线程安全的。当无法避免,必须要共享这个对象状态给多线程访问时,这时候才用到线程同步的一系列技术(锁)。 对象的状态:指存储在状态变量(eg:实例或者静态域)中的数据。 处理多线程的大方向:如果能避免多线程问
Java并发基础(三)-线程安全
1. 什么是线程安全 如果一个对象可以安全的被多个线程同时使用,那么它就是线程安全的 当多个线程访问一个对象的时候,如果不考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。 2. Java中的线程安全根据线程安全的安全程度由强至弱来排序,可以分为5类: 不可变 绝对线程安全 相对
java并发编程实践_02线程安全
1、原子性: 假设有操作A和B, 如果从执行A的线程的角度看, 当其他线程执行B时, 要么B全部执行完成, 要么一点都没有执行, 这样A和B互为原子操作, 一个原子操作是指: 该操作对于所有的操作, 包括它自己, 都满足前面描述的状态. 原子性的含义与它在事务性应用中相同->一组语句作为单独的, 不可分割的单元运行.2、synchronized: 一个synchronized块有两部分, 锁对
java并发之内存模型与线程安全-02
原子性 指一个操作是不可中断的,即使是在多线程一起执行的时候,一个操作一旦开始, 就不会被其他线程干扰。 可见性 是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够 立即看得到修改的值。于可见性,Java提供了volatile关键字来保证可见性。 如果一个变量被volatitle修饰后,当一个线程对这个变量进行修改时,会将修改后 的值更新到主内存中,如果有其他线程想要...
java并发编程实战(二)—线程安全
1 volatile变量: volatile是一种比synchronized关键字更轻量级的同步机制。 当线程A首先写入一个volatile变量并且线程B随后读取该变量时,在写入volatile变量 之前对A可见的所有变量的值,在B读取了volatile变量后,对B是可见的。因此,从内存 可见性的角度来看,写入volatile变量相当于退出同步代码块,而读取volatile变量相当