精华内容
下载资源
问答
  • 线程同时共享,同一个全局变量或者静态变量,做写得操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。 举个例子,同时售卖火车票 package ...
    1. 理解线程安全?
    2. synchronized用法
    3. 死锁
    4. Java内存模型
    5. Vlolatile
    6. ThreadLock

    什么是线程安全

    当多个线程同时共享,同一个全局变量或者静态变量,做写得操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。

    举个例子,同时售卖火车票

    package com.evan.springboot.concurrentDemo.ticket;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:01
     */
    public class ThreadTran implements Runnable {
        private int count=100;
        private static Object object=new Object();
        @Override
        public void run() {
            while (count>0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sale();
            }
        }
    
        public void sale(){
            System.out.println(Thread.currentThread().getName()+",正在出售第"+(100-count+1));
            count--;
        }
    }
    
    
    package com.evan.springboot.concurrentDemo.ticket;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:02
     */
    public class Demo {
        public static void main(String[] args) {
            ThreadTran threadTran = new ThreadTran();
            Thread t1 = new Thread(threadTran, "一号窗口");
            Thread t2 = new Thread(threadTran, "二二");
            t1.start();
            t2.start();
        }
    }
    
    

    在这里插入图片描述
    一号窗口和二号窗口同时出售火车第一张和第七张,部分火车票会重复出售。
    结论发现,多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。

    线程安全问题的解决办法:

    问:如何解决多线程之间线程安全问题?
    答:使用多线程之间同步synchronized或使用锁(lock)。
    问:为什么使用线程同步或使用锁能解决线程安全问题呢?
    答:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。
    问:什么是多线程之间同步?
    答:当多个线程共享同一个资源,不会受到其他线程的干扰。

    package com.evan.springboot.concurrentDemo.ticket;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:01
     */
    public class ThreadTran implements Runnable {
        private int count=100;
        private static Object object=new Object();
        @Override
        public void run() {
            while (count>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sale();
            }
        }
    
        public void sale(){
            if (count>0) {
                synchronized (object) {
                    if (count > 0) {
                        System.out.println(Thread.currentThread().getName() + ",正在出售第" + (100 - count + 1));
                        count--;
                    }
                }
            }
        }
    }
    
    

    多线程死锁

    • 死锁的定义:

    多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

    • 死锁产生的原因
    1. 系统资源的竞争
      通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争 才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。

    2. 进程推进顺序非法
      进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都 会因为所需资源被占用而阻塞。

    信号量使用不当也会造成死锁。进程间彼此相互等待对方发来的消息,结果也会使得这 些进程间无法继续向前推进。例如,进程A等待进程B发的消息,进程B又在等待进程A 发的消息,可以看出进程A和B不是因为竞争同一资源,而是在等待对方的资源导致死锁。

    1. 死锁产生的必要条件
      产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。

    互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
    不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
    请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
    循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, …, pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有,如图2-15所示。

    锁顺序死锁

    package com.evan.springboot.concurrentDemo.DeadLock;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:19
     */
    public class LeftRightDeadLock {
        private final static Object left=new Object();
        private final Object right=new Object();
        public boolean flag=true;
    
    
        public void leftRight() throws InterruptedException {
            synchronized (left){
                Thread.sleep(200);
                synchronized (right){
                    Thread.sleep(60);
                }
            }
        }
    
        public void rightLeft() throws InterruptedException {
            synchronized (right){
                Thread.sleep(200);
                synchronized (left){
                    Thread.sleep(60);
                }
            }
        }
    
    }
    
    
    package com.evan.springboot.concurrentDemo.DeadLock;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:21
     */
    public class Demo {
        public static void main(String[] args) {
            LeftRightDeadLock leftRightDeadLock = new LeftRightDeadLock();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        leftRightDeadLock.leftRight();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        leftRightDeadLock.rightLeft();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
    
    
    package com.evan.springboot.concurrentDemo.DeadLock;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:27
     */
    public class DeadLockDemo implements Runnable {
        public int flag = 1;
        //静态对象是类的所有对象共享的
        private static Object o1 = new Object(), o2 = new Object();
    
        @Override
        public void run() {
            System.out.println("flag=" + flag);
            if (flag == 1) {
                synchronized (o1) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized (o2) {
                        System.out.println("1");
                    }
                }
            }
            if (flag == 0) {
                synchronized (o2) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized (o1) {
                        System.out.println("0");
                    }
                }
            }
        }
    
        public static void main(String[] args) {
    
            DeadLockDemo td1 = new DeadLockDemo();
            DeadLockDemo td2 = new DeadLockDemo();
            td1.flag = 1;
            td2.flag = 0;
            //td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
            //td2的run()可能在td1的run()之前运行
            new Thread(td1).start();
            new Thread(td2).start();
    
        }
    }
    
    

    如何避免死锁

    在有些情况下死锁是可以避免的。三种用于避免死锁的技术:

    加锁顺序(线程按照一定的顺序加锁)
    加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
    死锁检测

    多线程的三大特性

    原子性
    可见性
    有序性

    • 原子性

    即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
    一个很经典的例子就是银行账户转账问题:
    比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。
    我们操作数据也是如此,比如i = i+1;其中就包括,读取i的值,计算i,写入i。这行代码在Java中是不具备原子性的,则多线程运行肯定会出问题,所以也需要我们使用同步和lock这些东西来确保这个特性了。
    原子性其实就是保证数据一致、线程安全一部分,

    • 可见性
      当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
      若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改线程没看到这就是可见性问题。
    • 有序性

    程序执行的顺序按照代码的先后顺序执行。
    一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下:
    int a = 10; //语句1
    int r = 2; //语句2
    a = a + 3; //语句3
    r = a*a; //语句4
    则因为重排序,他还可能执行顺序为 2-1-3-4,1-3-2-4
    但绝不可能 2-1-4-3,因为这打破了依赖关系。
    显然重排序对单线程运行是不会有任何问题,而多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

    java内存模型

    共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
    在这里插入图片描述
    从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

    1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
    2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。
      在这里插入图片描述
      如上图所示,本地内存A和B有主内存中共享变量x的副本。假设初始时,这三个内存中的x值都为0。线程A在执行时,把更新后的x值(假设值为1)临时存放在自己的本地内存A中。当线程A和线程B需要通信时,线程A首先会把自己本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。
      从整体来看,这两个步骤实质上是线程A在向线程B发送消息,而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。

    总结:什么是Java内存模型:java内存模型简称jmm,定义了一个线程对另一个线程可见。共享变量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。

    Volatitle

    Volatile 关键字的作用是变量在多个线程之间可见。

    package com.evan.springboot.concurrentDemo;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 11:28
     */
    public class ThreadVolatitleDemo extends Thread {
    
        public boolean flag=true;
        @Override
        public void run(){
            System.out.println("开始执行子线程");
            while (flag){
    
            }
            System.out.println("线程停止");
        }
    
        public void setRunging (boolean flag){
            this.flag=flag;
        }
    
        public static void main(String[] args) throws InterruptedException {
            ThreadVolatitleDemo demo = new ThreadVolatitleDemo();
            demo.start();
            Thread.sleep(3000);
            demo.setRunging(false);
            System.out.println("flag 已经设置成false");
            Thread.sleep(1000);
            System.out.println(demo.flag);
    
        }
    }
    
    

    在这里插入图片描述
    已经将结果设置为fasle为什么?还一直在运行呢。
    原因:线程之间是不可见的,读取的是副本,没有及时读取到主内存结果。
    解决办法使用Volatile关键字将解决线程之间可见性, 强制线程每次读取该值的时候都去“主内存”中取值

    Volatitle非原子性

    package com.evan.springboot.concurrentDemo;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 11:35
     */
    public class ThreadNoAtomic extends Thread {
        private static volatile int count;
    
        private  static void addCount(){
            for (int i = 0; i < 1000; i++) {
                count++;
            }
            System.out.println(count);
        }
    
        @Override
        public void run() {
            addCount();
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new ThreadNoAtomic().start();
            }
        }
    }
    
    

    在这里插入图片描述
    使用AtomicInteger原子类

    package com.evan.springboot.concurrentDemo;
    
    import sun.java2d.pipe.SpanIterator;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 11:40
     */
    public class ThreadAtomicInteger extends Thread {
    
        private static AtomicInteger atomicInteger=new AtomicInteger(0);
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                atomicInteger.incrementAndGet();
            }
            System.out.println(atomicInteger.get());
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new ThreadAtomicInteger().start();
            }
        }
    }
    
    

    volatile与synchronized区别
    仅靠volatile不能保证线程的安全性。(原子性)
    ①volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法
    ②volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。
    synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。

    ThreadLocal

    什么是ThreadLocal
    ThreadLocal提高一个线程的局部变量,访问某个线程拥有自己局部变量。
    当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
    ThreadLocal的接口方法
    ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
    void set(Object value)设置当前线程的线程局部变量的值。
    public Object get()该方法返回当前线程所对应的线程局部变量。
    public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
    protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

    package com.evan.springboot.concurrentDemo.ThreadLocalDemo;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 11:50
     */
    public class Res {
        public static Integer count=0;
    
        public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
            protected Integer initialValue() {
                return 0;
            };
    
        };
    
        public Integer getNum(){
            threadLocal.set(threadLocal.get()+1);
            return  threadLocal.get();
        }
    }
    
    
    package com.evan.springboot.concurrentDemo.ThreadLocalDemo;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 12:03
     */
    public class ThreadLocalDemo extends Thread {
        private Res res;
        public ThreadLocalDemo(Res res){
            this.res=res;
        }
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + "---" + "i---" + i + "--num:" + res.getNum());
    
            }
        }
    
        public static void main(String[] args) {
            Res res = new Res();
            ThreadLocalDemo threadLocaDemo1 = new ThreadLocalDemo(res);
            ThreadLocalDemo threadLocaDemo2 = new ThreadLocalDemo(res);
            ThreadLocalDemo threadLocaDemo3 = new ThreadLocalDemo(res);
            threadLocaDemo1.start();
            threadLocaDemo2.start();
            threadLocaDemo3.start();
        }
    }
    
    

    在这里插入图片描述
    ThreadLoca实现原理
    ThreadLoca通过map集合
    Map.put(“当前线程”,值);

    展开全文
  • 本书主要介绍在Java语言中使用多线程技术,首先介绍一下线程的概念:线程可以理解为在进程中独立执行的子任务,而进程是操作系统管理的基本运行单元。使用多线程技术,可以在同一时间内运行各种不同的任务,就好像你...

    第一章

    本书主要介绍在Java语言中使用多线程技术,首先介绍一下线程的概念:线程可以理解为在进程中独立执行的子任务,而进程是操作系统管理的基本运行单元。使用多线程技术,可以在同一时间内运行各种不同的任务,就好像你在windows系统中同时打开多个软件,同时进行聊天、听音乐、写代码等多个操作,更好的利用资源。

    1.1 实现

    目前来说,实现多线程编程方式主要有两种:1.继承Thread类2.实现Runnable接口。
    这两者的主要区别在于,使用第一种方式不支持多继承,第二种方式支持。

    1.2 run和start方法

    多线程编程中,最主要关注的就是Thread类,其中有两个方法start和run,这两者的关系是这样的,通过调用start方法通知线程规划期这个线程已经准备就绪,等待调用线程对象中的run方法,而run方法中一般是我们需要运行的代码。

    1.3 实例变量和线程安全

    我们自定义的线程类中的实例变量针对其他线程可以有共享和不共享之分,多线程编程中这个是一定要注意的一点。
    共享数据就是多个线程可以访问一个变量,比如抢票,抢红包等功能中需要。这就需要该变量属于线程安全的变量,就是说多个线程访问同一个对象的同一个实例变量进行操作时,值是要同步更新的,保证每个线程访问到的值相同。这就会引入一个锁的概念,同一时间内只有一个线程能获得锁对变量进行操作,保证了安全性,

    1.4 停止线程

    多线程开发带来了便利,但对于开发人员来说也有一些不便之处,代码中一旦处理不好就会出现超出预期的行为,并且难以定位这种错误。所以停止线程是很有必要的。
    主要介绍了三种停止线程的方法:
    1.使用退出标志,线程正常退出(run方法完成后)
    2.使用stop方法强行终止(不推荐JAVA中已经过期)
    3.使用interrupt方法中断线程

    1.5 暂停线程

    Suspend()暂停,resume()回复
    缺点:容易造成公共的同步对象被独占,而且也容易出现因为线程暂停导致的数据不同步

    1.6 线程的优先级

    操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多。我们可以手动设置优先级来帮助线程规划器确定下一次优先执行哪一个线程。
    优先级具有继承性,也就是说如果A线程中启动了B线程,那么AB线程的优先级是一样的。
    优先级具有规则性,高优先级的线程大部分先执行完。
    优先级具有随机性,上一条所说的大部分先执行完,但并不代表高优先级的会被全部先执行完,线程的执行顺序只和调用顺序有关,与优先级无关。

    1.7 守护线程

    JAVA线程可以分为两种:用户线程和守护线程,守护线程比较特殊,如果进程中不存在非守护线程了,那么守护线程就会自动销毁,典型的就是垃圾回收线程。守护线程的作用就是为其他线程的运行提供便利的服务。

    小结:第一章主要介绍了多线程的概念,还有Thread类中的API方法,属于基础内容,需要牢牢掌握。

    展开全文
  • 多线程(一)高并发和多线程的关系  “高并发和多线程”总是被一起提起,给人感觉两者好像相等,实则 高并发 ≠ 多线程  多线程是完成任务的一种方法,高并发是系统运行的一种状态,通过多线程有助于系统承受高...

    多线程(一)高并发和多线程的关系

         “高并发和多线程”总是被一起提起,给人感觉两者好像相等,实则 高并发 ≠ 多线程

      多线程是完成任务的一种方法,高并发是系统运行的一种状态,通过多线程有助于系统承受高并发状态的实现。

       高并发是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问或者socket端口集中性收到大量请求(例如:12306的抢票情况;天猫双十一活动)。该情况的发生会导致系统在这段时间内执行大量操作,例如对资源的请求,数据库的操作等。如果高并发处理不好,不仅仅降低了用户的体验度(请求响应时间过长),同时可能导致系统宕机,严重的甚至导致OOM异常,系统停止工作等。如果要想系统能够适应高并发状态,则需要从各个方面进行系统优化,包括,硬件、网络、系统架构、开发语言的选取、数据结构的运用、算法优化、数据库优化……而多线程只是其中解决方法之一

       实现高并发需要考虑:
                    系统的架构设计,如何在架构层面减少不必要的处理(网络请求,数据库操作等)
                    网络拓扑优化减少网络请求时间、如何设计拓扑结构,分布式如何实现?
                    系统代码级别的代码优化,使用什么设计模式来进行工作?哪些类需要使用单例,哪些需要尽量减少new操作?
                    提高代码层面的运行效率、如何选取合适的数据结构进行数据存取?如何设计合适的算法?
                    任务执行方式级别的同异步操作,在哪里使用同步,哪里使用异步?
                    JVM调优,是以server模式还是以clien模式运行,如何设置Heap、Stack、Eden的大小,如何选择GC策略,控制Full GC的频率?
                    数据库优化减少查询修改时间。数据库的选取?数据库引擎的选取?数据库表结构的设计?数据库索引、触发器等设计?是否使用读写分离?还是需要考虑使用数据仓库?
                    缓存数据库的使用,如何选择缓存数据库?是Redis还是Memcache? 如何设计缓存机制?
                    数据通信问题,如何选择通信方式?是使用TCP还是UDP,是使用长连接还是短连接?NIO还是BIO?netty、mina还是原生socket?
                    操作系统选取,是使用winserver还是Linux?或者Unix?
                    硬件配置?是8G内存还是32G,网卡10G还是1G?
                    ……
                    ……

    以上的这些问题在高并发中都是必须要深入考虑的,就像木桶原理一样,只要其中的某一方面没有考虑到,都会造成系统瓶颈,影响整个系统的运行。而高并发问题不仅仅涉及面之广,同时又要求有足够的深度!!!

       而多线程在这里只是在同/异步角度上解决高并发问题的其中的一个方法手段,是在同一时刻利用计算机闲置资源的一种方式。

    多线程在解决高并发问题中所起到的作用就是使计算机的资源在每一时刻都能达到最大的利用率,不至于浪费计算机资源使其闲置。
     

    ==========================================

    原文链接:多线程(一)高并发和多线程的关系转载请注明出处!

    ==========================================

    ---end

    展开全文
  • 多线程作为java中重量级的知识,几乎在所有的面试中都会被问及,好多小伙伴只知道多线程重要,却不知道,多线程在什么情况下会发生,以及发生了需要怎样去解决,下面案例通过12306抢购最后一张票来展示多线程并发会...

    多线程作为java中重量级的知识,几乎在所有的面试中都会被问及,好多小伙伴只知道多线程重要,却不知道,多线程在什么情况下会发生,以及发生了需要怎样去解决,下面案例通过12306抢购最后一张票来展示多线程并发会导致的结果,以及怎样简单的去处理多线程引发的资源抢夺问题!

    代码如下:

    public class Test {
     public static void main(String[] args) {
            //某列次车剩余票数
        Account myAccount = new Account(1, "列次A车");
        //用户A开始抢票!
            Drawing you = new Drawing(myAccount, 1, "你");
            //用户B开始抢票!
            Drawing yourWife = new Drawing(myAccount, 1, "你老婆");
            you.start();
            yourWife.start();
        }
    }
    //某列次车
    class Account {
      int    ps;//剩余票数
      String name;//卡名
     
      public Account(int ps, String name) {
          this.ps = ps;
          this.name = name;
      }
    }
     
    //12306:模拟抢票
    class Drawing extends Thread {
      Account account;//某列次车
      int     drawingPs;//取了多少张票
      int     nowPs;//现在手里有多少张票
     
      public Drawing(Account account, int drawingPs,String name) {
          super(name);
          this.account = account;
          this.drawingPs = drawingPs;
     
      }
     
      //synchronized默认锁的this.即Drawing
      @Override
      public void run() {
     
          //锁的对象是变化的量,需要增删改的对象
         // synchronized (account) {
              //判断余额是否充足
              if (drawingPs > account.ps) {
                  System.out.println(Thread.currentThread().getName() + ":剩余票数不足!");
                  return;
              }
              //模拟延时 sleep可以放大问题的发生性
                  try {
    this.sleep(1000);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
     
              //余票 = 总票 - 取完的票
              account.ps = account.ps - drawingPs;
              System.out.println(account.name + "余票为" + account.ps);
              nowPs = nowPs + drawingPs;
              System.out.println(this.getName() + ":手里的票" + nowPs);
     
          //}
     
      }
    }

    上面代码中存在两个分线程和一个主线程同时执行,在没有经过任何多线程资源抢夺处理的情况下,打印结果如下:

     

    阿里P8大佬首次透露,12306是这样使用多线程处理高并发的

     

    通过结果可以看出,尽管该列次的车票只剩了1张,但是线程A和线程B还是都买到了票,而该列次剩余的票却显示为-1张,这是不正常的,为了解决以上的问题我们可以将代码中的synchronized 锁定的资源代码来进行释放解决以上出现的问题

    阿里P8大佬首次透露,12306是这样使用多线程处理高并发的

     

    释放完这两行代码之后,在执行程序发现不会再发生该列次车的剩余票数为负数的情况

     

    阿里P8大佬首次透露,12306是这样使用多线程处理高并发的

     

     

    上面是通过synchronized 来简单解决多线程资源抢夺的问题,金三银四,希望对你的面试有所帮助,加油,小伙伴们!

    展开全文
  • Java基础5多线程技术

    2018-12-04 17:52:22
    多线程是Java语言的重要特性,大量应用于网络编程、服务器端程序的开发,最常见的UI界面底层原理、操作系统底层原理都大量使用了多线程。 我们可以流畅的点击软件或者游戏中的各种按钮,其实,底层就是多线程的应用...
  • 多线程 1、创建线程之继承Thread类 1、定义类继承Thread类 2、重写run()方法 3、调用自定义类的start()方法,启动线程 1.1、继承Thread类实现窗口抢票的功能 class SaleTicket extends Thread{ public SaleTicket...
  • 线程同步 三大线程不安全案例 一、多人同时抢票问题 二、
  • 什么是线程安全?为什么有线程安全问题?...案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。 代码: public class ThreadTrain implements Runnable { private i...
  • 1.“高并发和多线程”总是被一起提起,感觉两者好像相等,实则 高并发 ≠ 多线程 (1)要想系统能够适应高并发状态,则需要从各个方面进行系统优化 (2)硬件、网络、系统架构、开发语言的选取、数据结构的运用、...
  • 高并发(High Concurrency)是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问收到大量请求(例如:12306的抢票情况;天猫双十一活动)。该情况的发生会导致系统在...
  • 多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。 多进程是指操作系统能同时运行多个任务(程序)。 多线程是指在同一程序中有多个顺序流在执行。 什么是线程什么是进程? 1)进程:每...
  • 多线程和高并发介绍

    万次阅读 2021-06-06 05:00:46
    多线程和高并发介绍 文章目录多线程和高并发介绍前言一、什么是多线程?1.多线程介绍2.多线程实现原理?3.白话文解释多线程4.多线程存在的问题二、什么是高并发?1.高并发介绍2.如何提升系统的并发能力三、多线程和高...
  • 高并发(High Concurrency)是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问收到大量请求(例如:12306的抢票情况;天猫双十一活动)。该情况的发生会导致系统在...
  • 高并发多线程简介

    2020-12-15 10:55:24
    高并发多线程简介 一、什么是高并发   高并发(High Concurrency)是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问收到大量请求(例如:12306的抢票情况;...
  • 并发编程之多线程线程安全 什么是线程安全?...案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。 代码: public class ThreadTrain implements Runnable { pr...
  • 多线程线程安全问题

    2019-12-13 15:44:00
    线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。         经典案例...
  • iOS边城之多线程并发编程

    千次阅读 2015-01-05 10:57:27
    基本每门语言都有多线程这个技术点,多线程是为了实现并发执行,可以理解为一个系统进程是由一个或多个线程组成的。iOS中创建线程的方式简单到可以直接调用对象的方法来实现,下面我们来看看。   调用NSObject方法...
  • java多线程笔记1

    2020-05-10 07:20:36
    1 初识线程 public class HelloMain { public static void main(String[] args) { ...使用多线程 打开源码我们发现 Thread类竟然实现类Runnable接口。 所以我们可以这样创建线程 public class HelloMain
  • 什么是线程安全? 为什么有线程安全问题?...案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。 public class ThreadTrain implements Runnable { private int t...
  • 初学Java多线程系列的本部分介绍线程的生命周期。与人有生老病死一样,线程也同样要经历开始(等待)、运行、挂起和停止四种不同的状态。这四种状态都可以通过Thread类中的方法进行控制。 AD: 2013大数据全球技术...
  • JAVA多线程买票简化版

    2020-05-15 16:23:45
    } //上锁简化,这里这可有其它关键字或更复杂的技术手段应对实际场景 public void sale() { synchronized (ThreadTicket.class) { if(ticketList.size()>0) { System.out.println(buyer+":"+ticketList.get(0));...
  • 多线程

    2021-03-22 18:23:18
    可见性(Visibility):是指一个线程对共享变量进行修改,另一个线程立即得到修改后的最新值 public class Demo{ private static boolean flag=true; public sttaic void mian(Stirng[] args){ new Thread(()->...
  • 多线程基础详解

    2020-01-22 15:11:33
    JUC多线程 (一) ...​ 一个采用了多线程技术的应用程序可以更好地利用系统资源。其主要优势在于充分利用了CPU的空闲时间片,可以用尽可能少的时间来对用户的要求做出响应,使得进程的整体运行效率...
  • 文章目录Java从入门到实战总结-3.4、Java多线程1、多线程技术概述(1)、线程与进程(2)、线程调度(3)、异步和同步(4)、并发和并行2、继承Thread实现线程3、实现Runnable接口实现线程4、Thread类(1)、类方法...
  • 什么是线程安全? 为什么有线程安全问题?...案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。 代码: public class ThreadTrain implements Runnable { ...
  • 多线程的学习从一些概念开始,进程和线程,并发与并行,同步与异步,高并发。 1.1 进程与线程 几乎所有的操作系统都支持同时运行期多个任务,所有运行中的任务通常就是一个进程,进程是处于运行过程中的程序,进程...
  • 多线程快速入门 一.1.面试题 一.2.多线程创建方式 一.3.多线程运行状态 一.4.jion()和Yield() 二. 多线程线程安全 二.1.什么是线程安全,线程安全的解决办法,死锁 二.2.多线程三大特性 二.3.Java...
  •   上面说到了多线程的简单实现,编写了几个入门的小例子,这里我们来研究一下关于实例变量和线程安全的问题。在自定义的线程类中的实例变量针对其他线程可以有共享和不共享之分,下多个线程之间进行交互的时候会...
  • Java学习——多线程一 一些概念二 继承Thread三 实现Runnable四,Thread类1 设置与获取线程名称2 线程休眠sleep3 线程中断五 线程不安全1,现象与原因2,解决方案(1)同步代码块(2)同步方法(3)显式锁六 线程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,186
精华内容 874
关键字:

多线程抢票技术