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();
}
}
标签:tickets,Java,Thread,张票,线程,new,多线程,public 来源: https://www.cnblogs.com/atao-BigData/p/16133449.html