Java学习笔记45(多线程二:安全问题以及解决原理)

线程安全问题以及解决原理:

多个线程用一个共享数据时候出现安全问题

一个经典案例:

电影院卖票,共有100座位,最多卖100张票,买票方式有多种,网上购买、自主售票机、排队购买

三种方式操作同一个共享数据,这时候会出现安全问题:

示例:

package demo1;

public class Tickets implements Runnable {
    private int ticket = 100;

    public void run(){
        while(true){
            if (ticket>0) {
                System.out.println(Thread.currentThread().getName()+"出售第"+ticket--+"张票");
            }
        }
    }
}
package demo1;

public class ThreadDemo {
    public static void main(String[] args) {
        Tickets t = new Tickets();
        Thread t0 = new Thread(t);
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t0.start();
        t1.start();
        t2.start();
    }
}

一般不会出现问题,但是要想到这种问题

但是,假设只剩下最后最后一张票,一个线程抢到CPU资源执行,在判断结束时候,CPU资源被其他线程抢到,其他线程判断然后执行,

这时候轮到开始时候的线程,由于已经判断完,继续执行,这时候票数就会变成负数,这里就出现了问题

解决方法:

同步代码块

原理:一个线程进入数据操作的时候,阻止其他线程执行

package demo1;

public class Tickets implements Runnable {
    private int ticket = 100;
    private Object obj1 = new Object();
    public void run() {
        while (true) {
            synchronized (obj1) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
                }
            }
        }
    }
}

不过,虽然安全了,但是运行速度下降

但是,我们为了安全性可以不顾及速度,无论如何都要保证安全性

这里传入的对象参数简称作:同步锁,专业名称:对象监视器

原理:

没有锁的线程不能执行,只能等待

线程遇到同步代码块后判断是否有同步锁,如果有,拿走锁,进入同步中执行,执行完毕后将锁对象还回去

另一个线程遇到代码块后没有锁,无法进入,原来的线程把锁还回去之后新线程再获取锁,循环下去

这里明显可以看出,这么多的过程,速度自然就慢下来了

采用同步方法解决问题:

优点:代码量更低

package demo1;

public class Tickets implements Runnable {
    private int ticket = 100;

    public void run() {
        while (true) {
            payTicket();
        }
    }

    public synchronized void payTicket() {
        //同步方法的对象锁是本类对象引用:即为this
        //静态方法的锁是本类类名.class
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
        }
    }
}

缺点:如果出现了异常,方法的锁对象没有释放,不出同步,锁不会释放

这里就需要用到一个Lock接口:

提供了更广泛的锁定操作

改进之前的售票案例:

package demo1;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Tickets implements Runnable {
    private int ticket = 100;
    private Lock lock = new ReentrantLock();

    public void run() {
        while (true) {
            lock.lock();
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
            }
            lock.unlock();
        }
    }
}

死锁:

同步锁引发的弊端:

当线程任务中出现了多个同步时,如果同步中嵌套了其他的同步,这时候就会引发一种现象,程序出现无限等待,这种现象称之为死锁

通俗解释:两个人吃一碗面,却只有一双筷子,两个人一人抢到一支筷子,规定不能用手抓,这时候就无法吃面

代码实现:

package demo1;

public class LockA {
    private LockA(){}

    public final static LockA locka =new LockA();
}
package demo1;

public class LockB {
    private LockB(){}

    public final static LockB lockb =new LockB();
}
package demo1;

public class DeadLock implements Runnable {
    private int i = 0;

    public void run() {
        while (true) {
            if (i % 2 == 0) {
                synchronized (LockA.locka) {
                    System.out.println("if...locka");
                    synchronized (LockB.lockb) {
                        System.out.println("if...lockb");
                    }
                }
            } else {
                synchronized (LockB.lockb) {
                    System.out.println("else...lockb");
                    synchronized (LockA.locka) {
                        System.out.println("else...locka");
                    }
                }
            }
            i++;
        }
    }
}
package demo1;

public class DeadLockDemo {
    public static void main(String[] args) {
        DeadLock dead = new DeadLock();
        Thread t0 = new Thread(dead);
        Thread t1 = new Thread(dead);
        t0.start();
        t1.start();
    }
}

运行后发现,会卡在某一处不动,但是并没有停止

原文地址:https://www.cnblogs.com/xuyiqing/p/8320162.html

时间: 2024-10-12 22:22:06

Java学习笔记45(多线程二:安全问题以及解决原理)的相关文章

Java学习笔记之多线程二

看到一篇讲线程的故事性文章,觉得很有意思,很佩服作者能这么生动地讲述出来,点击可跳转阅读此文章:<我是一个线程> 继续我的笔记中总结 - - 理解线程安全问题: 下面是书上看到的卖票例子:模拟3个窗口同时在售10张票. 上篇博文笔记总结了多线程创建的两种方式,那我们就分别以这两种实现多线程的方式来解决这个场景. 使用继承于Thread类的方式 上Demo: class SaleTicket extends Thread { int num = 10; // 票数 public SaleTick

马哥学习笔记三十二——计算机及操作系统原理

缓存方式: 直接映射 N路关联 缓存策略: write through:通写 write back:回写 进程类别: 交互式进程(IO密集型) 批处理进程(CPU密集型) 实时进程(Real-time) CPU: 时间片长,优先级低IO:时间片短,优先级高 Linux优先级:priority 实时优先级: 1-99,数字越小,优先级越低 静态优先级:100-139,数据越小,优先级越高 实时优先级比静态优先级高 nice值:调整静态优先级   -20,19:100,139   0:120 ps

黑马程序员——JAVA学习笔记六(多线程)

1,    什么是多线程?一个程序可以执行多个任务,每一个任务称为一个线程,运行多个线程的程序称为多线程程序. 进程:正在进行中的程序(直译). 线程:进程中一个负责程序执行的控制单元(执行路径).   多线程的好处:解决了多部分代码同时运行的问题.多线程的弊端:线程太多,会导致效率的降低. 其实,多个应用程序同时执行都是CPU在做着快速的切换完成的.这个切换是随机的.CPU的切换是需要花费时间的,从而导致了效率的降低 2 ,    创建线程方式:  创建线程方式一:继承Thread类 1.定义

java学习笔记(十二)多线程编程

并发编程:可以让多个任务同时运行的编程方式. 进程:一个正在运行的程序,有自己独立的一块内存空间,每一个进程的内部数据和状态都是完全独立的.可同时运行两个或更多的程序. 线程:每一个任务称为一个线程,在一个程序内运行多线程的程序称为多线程程序. 线程是进程内部单一的一个顺序控制流,是最小处理单位. 创建线程: 1.继承Thread类,重写run()方法,在run()方法中编写要执行的代码. 先创建Thread对象,打点调用start()启动线程. 2.实现Runable接口,只有run()方法.

Java学习笔记之多线程

/* 进程: 正在进行中的程序(直译). 线程: 就是进程中一个负责程序执行的控制单元(执行路径) 一个进程中可以有多个执行路径, 称之为多线程. 一个进程中至少要有一个线程. 开启多个线程是为了同时运行多部分代码. 每一个线程都有自己运行的内容. 这个内容可以称为线程要执行的任务. 多线程的好处: 解决了多部分同时运行的问题. 多线程的弊端: 线程太多回到效率的降低. 其实应用程序的执行都是cpu在做着快速的切换完成的. 这个切换是随机的. jvm启动时就启动了多个线程,至少有两个线程可以分析

Java学习笔记-8.多线程编程

一.引入线程 1.多线程和多进程的区别 (1)两者粒度不同,进程是由操作系统来管理,而线程则是在一个进程内 (2)每个进程是操作系统分配资源和处理器调度的基本单位,拥有独立的代码.内部数据和状态 而一个进程内的多线程只是处理器调度的基本单位,共享该进程的资源,线程间有可能相互影响 (3)线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担小 2.Thread类:Java的线程是通过java.lang.Thread类来实现,一个Thread对象代表一个线程

JAVA学习笔记之多线程专题(一):线程同步安全处理

关于多线程操作,我相信大家都不陌生,如何开启一个线程之类我想就不用太详细的去描述,今天我们就来讲讲线程同步的安全的问题. 对于线程同步安全问题,一般是一个多线程对同一个资源同时操作的时候,会出现资源同时操作造成线程不安全的问题.那么这个时候我们需要去对公共资源进行同步保护.这个时候有三种情况 1.同步代码块,这个同步的锁是任意一个对象: 2.方法同步,这个同步的锁就是该方法所在的类: 3.静态方法同步,这个同步的锁是该方法所在类的字节码. 接下来,我们举一个例子来说明多线程对同一个资源进行操作的

Java学习笔记—第十二章 Java网络编程入门

第十二章  Java网络编程入门 Java提供的三大类网络功能: (1)URL和URLConnection:三大类中最高级的一种,通过URL网络资源表达方式,可以很容易确定网络上数据的位置.利用URL的表示和建立,Java程序可以直接读入网络上所放的数据,或把自己的数据传送到网络的另一端. (2)Socket:又称"套接字",用于描述IP地址和端口(在Internet中,网络中的每台主机都有一个唯一的IP地址,而每台主机又通过提供多个不同端口来提供多种服务).在客户/服务器网络中,当客

黑马程序员——JAVA学习笔记十三(高新技术二)

8,    注解 Annotation(注解)是JDK5.0及以后版本引入的. 注解是以 @注解名 的形式标识 注解不会影响程序语义,只作为标识 注解是新的类型(与接口很相似),它与类.接口.枚举是在同一个层次,它们都称作为java的一个类型(TYPE). 它可以声明在包.类.字段.方法.局部变量.方法参数等的前面,用来对这些元素进行说明,注释. 它的作用非常的多,例如:进行编译检查.生成说明文档.代码分析等 注释类型 是一种特殊的接口 用 @interface 声明如 [email prote