没有合适的资源?快使用搜索试试~ 我知道了~
Java多线程--线程的安全问题与线程的同步机制介绍
资源推荐
资源详情
资源评论
一、线程安全问题
(1)介绍
(2)同一个资源问题和线程安全问题
1、方式一:实现Runnable接口
1.1 票数问题
1.2 重票和错票问题
2、方式二:继承Thread类
二、安全问题分类总结
(1)局部变量不能共享
(2)不同对象的实例变量不共享
(3)静态变量是共享的
(4)同一个对象的实例变量共享
(5)抽取资源类,共享同一个资源对象
一、线程安全问题
(1)介绍
当我们使用多个线程访问同一资源(可以是同一个变量、同一个文件、同一条记录等)的时候,若
多个线程 只有读操作 ,那么不会发生线程安全问题(因为不会对数据进行修改)。
但是如果多个线程中对资源有 读和写 的操作,就容易出现线程安全问题。
举例:
“多线程的安全问题”是因为多线程对统一资源进行读和写操作时带来的。
(2)同一个资源问题和线程安全问题
【案例】
火车站要卖票,我们模拟火车站的卖票过程。因为疫情期间,本次列车的座位共100个(即,只能
出售100张火车票)。我们来模拟车站的售票窗口,实现多个窗口同时售票的过程。
注意:不能出现错票、重票。
比如现在要开启三个窗口售票,总票数为100张。
1、方式一:实现Runnable接口
<1> 卖票
首先写一个卖票,用实现的方式建立一个线程,如下:
因为Runnable接口里面有抽象方法run,所以需要重写抽象方法, Ctrl+i 快捷键调出来,OK即
可,如下:
现在需要卖票,一共是100张票,所以需要有一个变量ticket来表示票的数量。
1.1 票数问题
🗳
这样写可以吗?
其实是不可以的,不能这样来表示100张票。
下一步我们需要做的是创建这个实现类的对象,具体创建线程的步骤如下:
class SaleTicket implements Runnable{ //卖票
}
class SaleTicket implements Runnable{ //卖票
@Override
public void run() {
}
}
class SaleTicket implements Runnable{ //卖票 1.创建一个实现Runnable接口的类(实现
类)
@Override
public void run() { //2.实现接口中的抽象方法run()方法
int ticket=100;
}
}
public class WindowTest {
public static void main(String[] args) {
//3.创建当前实现类的对象
SaleTicket s=new SaleTicket();
当整个程序跑起来之后,就会各自调用run方法,三个线程调用run方法,就会有300张票了。
所以下面的写法不靠谱。
只需要把ticket拿到 run() 方法外面定义即可,因为在main方法里面,只创建了一个对象s,它被
三个线程所共享了。
现在就没有问题了。
🌱
代码
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
Thread t3 = new Thread(s);
//给三个线程起名字
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//5.通过Thread类的实例调用start():1.启动线程 2.调用当前线程的run()。
t1.start();
t2.start();
t3.start();
}
}
public class WindowTest {
public static void main(String[] args) {
//3.创建当前实现类的对象
SaleTicket s=new SaleTicket();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
Thread t1 = new Thread(s);
<2> 售票
售票的逻辑当然是写在run方法里面的。
一共100张票,3个窗口,每个窗口不一定都拿到100/3张票,所以这里不知道循环了多少次,那就
用 while 循环吧,只要ticket大于0就说明还有票,若是ticket等于0就说明没有票了,退出循环即可。
如下:
Thread t2 = new Thread(s);
Thread t3 = new Thread(s);
//给三个线程起名字
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//5.通过Thread类的实例调用start():1.启动线程 2.调用当前线程的run()。
t1.start();
t2.start();
t3.start();
}
}
class SaleTicket implements Runnable{ //卖票 1.创建一个实现Runnable接口的类(实现
类)
int ticket=100;
@Override
public void run() { //2.实现接口中的抽象方法run()方法
}
}
class SaleTicket implements Runnable{ //卖票 1.创建一个实现Runnable接口的类(实现
类)
int ticket=100;
@Override
public void run() { //2.实现接口中的抽象方法run()方法
while (true){
if(ticket>0){ //如果票数大于0就可以售票
//哪个窗口卖票了,票卖了多少
System.out.println(Thread.currentThread().getName() + "售票,票号
为:" + ticket); //最开始票号为100
ticket--;
}else{
break;
}
}
}
}
🌱
代码
🍺
输出结果(部分)
package yuyi02.notsafe;
/**
* ClassName: WindowTest
* Package: yuyi02.notsafe
* Description:
*
* @Author 雨翼轻尘
* @Create 2024/1/27 0027 19:28
*/
public class WindowTest {
public static void main(String[] args) {
//3.创建当前实现类的对象
SaleTicket s=new SaleTicket();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
Thread t3 = new Thread(s);
//给三个线程起名字
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//5.通过Thread类的实例调用start():1.启动线程 2.调用当前线程的run()。
t1.start();
t2.start();
t3.start();
}
}
class SaleTicket implements Runnable{ //卖票 1.创建一个实现Runnable接口的类(实现
类)
int ticket=100;
@Override
public void run() { //2.实现接口中的抽象方法run()方法
while (true){
if(ticket>0){ //如果票数大于0就可以售票
//哪个窗口卖票了,票卖了多少
System.out.println(Thread.currentThread().getName() + "售票,票号
为:" + ticket); //最开始票号为100
ticket--;
}else{
break;
}
}
}
}
剩余24页未读,继续阅读
资源评论
雨翼轻尘
- 粉丝: 6w+
- 资源: 130
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功