Java-多线程2
2022/4/12 9:12:29
本文主要是介绍Java-多线程2,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
线程安全问题
先看一段代码
public class TicketWindow3 implements Runnable{ //由于这个类的对象只创建一次,也就只有一个对象,就只有一个tickets变量 private int tickets = 100; // 1 @Override public void run() { while (true) { //窗口1,窗口2 if (tickets > 0) { //窗口1,窗口2,窗口3 //为了模拟更加符合现实生活卖票的场景,我们加入延迟操作 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //窗口1,窗口2 System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票。。。"); //窗口1正在出售第100张票。。。 //窗口2正在出售第100张票。。。 //... //窗口1正在出售第1张票 //窗口2正在出售第0张票 //窗口3正在出售第-1张票 } } } }
这是创建线程的代码
/* 某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。 两种方式实现 2、实现Runnable接口 为了模拟更加现实的售票场景,我们加入延迟操作 但是呢,加入延迟之后,产生了两个问题: 1、相同的票我们卖了很多次 CPU的执行操作是原子性,小小的CPU时间片足矣运行很多次 2、出了卖第0张票和负数的票。 这个是由于线程的执行具有随机性和延迟性导致的,因为我们加入了sleep休眠方法,让线程变成阻塞状态,让其他线程执行 */ public class SellTicketDemo3 { public static void main(String[] args) { //创建自定义类对象 TicketWindow3 ticketWindow3 = new TicketWindow3(); //创建3个线程对象,模拟3个窗口进行卖票 Thread window1 = new Thread(ticketWindow3, "窗口1"); Thread window2 = new Thread(ticketWindow3, "窗口2"); Thread window3 = new Thread(ticketWindow3, "窗口3"); //启动线程 window1.start(); window2.start(); window3.start(); } }
出现问题
++为了模拟更加现实的售票场景,我们加入延迟操作但是呢,加入延迟之后,产生了两个问题:++
- 1、相同的票我们卖了很多次
CPU的执行操作是原子性,小小的CPU时间片足矣运行很多次- 2、出了卖第0张票和负数的票。
这个是由于线程的执行具有随机性和延迟性导致的,因为我们加入了sleep休眠方法,让线程变成阻塞状态,让其他线程执行
++上一个案例中加入了延迟操作,出现了问题,其实这个问题现象就叫做:线程安全问题要想解决这个问题,就得先搞清楚哪些原因导致的这个问题出现:(这三个条件同时满足的情况下,就会出现线程安全问题)++
- 1、是否存在多线程环境
- 2、是否存在共享变量(共享数据)
- 3、是否有多条语句操作着共享变量(共享数据)
解决办法
- 条件1,2都是我们无法改变的,我们只能想办法改变第3个条件,只要其中一个不满足,就不会发生线程安全问题
解决此问题的思想
- 要是有一个办法可以将多条操作着共享变量的代码包成一个整体,相当于给这段代码加了一把锁,在某个线程执行的时候,其他线程
进不来就可以了。
直到这个线程执行完后,其他线程才能进入。同一时刻,只能是一个线程操作run方法。
java提供了一个机制给我们使用,这个机制就是用来解决线程安全问题的同步安全机制
同步的好处和弊处:
- 同步的好处:
解决了多线程的安全问题
- 同步的弊端:
加入同步之后,就相当于加了一把锁,这样每次进入同步代码块的时候都会判断一下,
这样无形之中,降低了程序运行的效率。
解决方案一:
语句定义格式: synchronized(对象){ 需要同步的代码; } 同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。 1、这里的对象是什么? 我们先随便创建一个对象试试; 2、需要同步的代码又是哪些呢? 是操作共享变量的代
同步代码时,锁的对象是谁?
- 1、同步代码块的锁对象是谁?
任意对象,多个线程之间的锁对象要是唯一的。
- 2、同步方法所判断的锁对象是谁?
当前对象this
- 3、同步静态方法所判断的锁对象是谁?
当前线程类的对象
使用synchronized关键字上锁的代码如下:
package com.shujia.wyh.day26; /* 定义一个类实现Runnable接口 */ public class TicketWindow2 implements Runnable { //定义100张票 private static int tickets = 100; // private Object obj = new Object(); private Demo d = new Demo(); int i = 0; // @Override // public void run() { // while (true){ // synchronized (obj){ // if(tickets>0){ // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票。。"); // } // } // } // } // @Override // public void run() { // while (true) { // synchronized (d) { // if (tickets > 0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票。。"); // } // } // } // } @Override public void run() { while (true) { if (i % 2 == 0) { synchronized (TicketWindow2.class) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票。。"); } } } else { sellTickets(); } i++; } } // public synchronized void sellTickets() { // if (tickets > 0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票。。"); // } // } public synchronized static void sellTickets() { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票。。"); } } }
package com.shujia.wyh.day26; /* 1、同步代码块的锁对象是谁? 任意对象,多个线程之间的锁对象要是唯一的。 2、同步方法所判断的锁对象是谁? 当前对象this 3、同步静态方法所判断的锁对象是谁? 当前线程类的对象 */ public class SellTicketDemo2 { public static void main(String[] args) { //创建Runnable类对象 TicketWindow2 ticketWindow1 = new TicketWindow2(); //借助Thread类创建线程对象 Thread window1 = new Thread(ticketWindow1, "窗口1"); Thread window2 = new Thread(ticketWindow1, "窗口2"); Thread window3 = new Thread(ticketWindow1, "窗口3"); //启动线程 window1.start(); window2.start(); window3.start(); } }
解决线程安全问题方案二:使用Lock锁
注意点
- 多个线程对象拥有的锁对象要一致
代码如下:
package com.shujia.wyh.day26; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* 定义一个类实现Runnable接口 */ public class TicketWindow3 implements Runnable { //定义100张票 private int tickets = 100; // private Object obj = new Object(); private Lock lock = new ReentrantLock(); @Override public void run() { while (true) { lock.lock(); if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票。。"); } lock.unlock(); } } }
public static void main(String[] args) { //创建Runnable类对象 TicketWindow3 ticketWindow1 = new TicketWindow3(); //借助Thread类创建线程对象 Thread window1 = new Thread(ticketWindow1, "窗口1"); Thread window2 = new Thread(ticketWindow1, "窗口2"); Thread window3 = new Thread(ticketWindow1, "窗口3"); //启动线程 window1.start(); window2.start(); window3.start(); } }
死锁现象
问题产生原因:
- 两个或者两个以上的线程在争夺资源的过程中,出现了相互等待的现象,这个现象称之为死锁现象
举例说明:
- 中国人和外国人吃饭的案例:
正常情况下:
中国人:两支筷子
外国人:一把刀,一把叉
死锁的情况:
中国人:一支筷子,一把刀
外国人:一支筷子,一把叉
代码实现:
++先定义一个类,获取锁的对象++
public class MyLock { //创建两把锁 public static final Object lock1 = new Object(); public static final Object lock2 = new Object(); }
++发生死锁的代码:++
public class DieLock extends Thread{ private boolean flag; DieLock(boolean flag){ this.flag = flag; } @Override public void run() { if(flag){ synchronized (MyLock.lock1){ System.out.println("if lock1"); synchronized (MyLock.lock2){ System.out.println("if lock2"); } } }else { synchronized (MyLock.lock2){ System.out.println("else lock2"); synchronized (MyLock.lock1){ System.out.println("else lock1"); } } } } }
线程代码:
public class DieLockDemo { public static void main(String[] args) { //创建两个线程对象 DieLock d1 = new DieLock(true); DieLock d2 = new DieLock(false); //启动线程 d1.start(); d2.start(); } }
这篇关于Java-多线程2的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27消息中间件底层原理资料详解
- 2024-11-27RocketMQ底层原理资料详解:新手入门教程
- 2024-11-27MQ底层原理资料详解:新手入门教程
- 2024-11-27MQ项目开发资料入门教程
- 2024-11-27RocketMQ源码资料详解:新手入门教程
- 2024-11-27本地多文件上传简易教程
- 2024-11-26消息中间件源码剖析教程
- 2024-11-26JAVA语音识别项目资料的收集与应用
- 2024-11-26Java语音识别项目资料:入门级教程与实战指南
- 2024-11-26SpringAI:Java 开发的智能新利器