多线程2:线程的常用方法、线程安全
欢迎来到“雪碧聊技术”CSDN博客!
在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将不断探索Java的深邃世界,分享最新的技术动态、实战经验以及项目心得。
让我们一同在Java的广阔天地中遨游,携手提升技术能力,共创美好未来!感谢您的关注与支持,期待在“雪碧聊技术”与您共同成长!
目录
一、Thread的常用方法
1、Thread提供的常用方法
2、Thread提供的常见构造器
3、演示
①设置、获取线程名;获取当前线程
②让当前线程休眠1秒
③让线程插队
④借助Thread类的构造器,给线程取名字
4、说明
二、线程安全
1、认识线程安全
①定义
②举例
③总结
2、取钱案例 - 模拟线程安全问题
①案例需求分析
② 编写代码
3、总结
一、Thread的常用方法
1、Thread提供的常用方法
2、Thread提供的常见构造器
3、演示
①设置、获取线程名;获取当前线程
public class Test5 {
public static void main(String[] args) {
//目标:搞清楚线程的常用方法
My_Thread t1 = new My_Thread();
t1.setName("线程1");//设置t1线程的名称
System.out.println(t1.getName());//线程默认名称:Thread-索引,如Thread_0
t1.start();//启动线程2
My_Thread t2 = new My_Thread();
t2.setName("线程2");//设置t2线程的名称
System.out.println(t2.getName());//线程默认名称:Thread-索引,如Thread_0
t2.start();//启动线程2
//哪个线程执行此行代码,当前线程就是哪个线程
Thread thread = Thread.currentThread();//获取当前的线程是哪个
System.out.println(thread.getName());//当前线程是:主线程main
}
}
class My_Thread extends Thread{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程输出:"+i);
}
}
}
运行结果:
线程1
线程2
main
子线程输出:0
子线程输出:0
子线程输出:1
子线程输出:1
子线程输出:2
子线程输出:2
子线程输出:3
子线程输出:3
子线程输出:4
子线程输出:4
②让当前线程休眠1秒
public class Test6 {
public static void main(String[] args) {
//目标:搞清楚Thread类的Sleep方法(线程休眠)
for (int i = 0; i < 10; i++) {
System.out.println(i);
try {
//让当前线程休眠1秒
Thread.sleep(1000);//1000ms = 1s
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
③让线程插队
public class Test7 {
public static void main(String[] args) {
//目标:搞清楚线程的join方法:线程插队,即调用该方法的线程会先执行完毕
MyThread t1 = new MyThread("线程1");
t1.start();
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() +"-"+ i);
if(i==2){
try {
t1.join();//让t1线程先插队执行完毕
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
//1、定义一个子类继承Thread类,成为一个线程类
class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
//2、重写Thread类的run方法
public void run() {
//3、在run方法中编写线程的任务代码(线程要干的活)
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"-" + i);
}
}
}
运行结果:
一旦主线程main输出2,那么此时就会让线程1插队执行,等执行完线程1,才会让主线程main继续执行。
因此观察到的现象就是:控制台打印main-2之后,就会发现接着打印线程1的结果,直到打印完所有关于线程1的结果,才会继续打印main的。
线程1-1
main-1
线程1-2
main-2
线程1-3
线程1-4
线程1-5
线程1-6
线程1-7
线程1-8
线程1-9
线程1-10
main-3
main-4
main-5
main-6
main-7
main-8
main-9
main-10
④借助Thread类的构造器,给线程取名字
4、说明
二、线程安全
1、认识线程安全
①定义
多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。
②举例
③总结
2、取钱案例 - 模拟线程安全问题
①案例需求分析
② 编写代码
public class Test8 {
public static void main(String[] args) {
//目标:模拟线程安全问题
//1、设计一个账户类:用于创建小明和小红的共同账户,存入10万
Account acc = new Account(100000);
//2、设计线程类:创建小明和小红两个线程,模拟小明和小红两人同时从账户取款10万
new DrawThread("小明", acc).start();
new DrawThread("小红", acc).start();
}
}
//银行账户类
class Account{
private double money;//账户余额
//构造器
public Account(double money) {
this.money = money;
}
//get/set
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
//toString
@Override
public String toString() {
return "Account{" +
"money=" + money +
'}';
}
//成员方法:小明、小红来取钱
public void drawMoney(double money) {
//拿到当前取钱的线程名称(小明/小红)
String name = Thread.currentThread().getName();
//判断余额是否足够
if(this.money >= money){
//余额足够,取钱
System.out.println(name + "取钱成功,吐出了" + money +"元成功!");
//更新余额
this.money -= money;
//打印当前剩下的账户余额
System.out.println(name + "取钱成功后,当前余额剩余"+ this.money + "元");
}else{
//余额不足
System.out.println(name + "取钱失败,余额不足");
}
}
}
//取钱线程类
class DrawThread extends Thread{
private Account acc;//记住线程对象要处理的账户对象
//有参构造器
public DrawThread(String name, Account acc) {
super(name);//调用父类Thread类的有参构造器,来进行线程名称的赋值
this.acc = acc;
}
public void run() {
//小明、小红 取钱
acc.drawMoney(100000);
}
}
运行结果:
小明取钱成功,吐出了100000.0元成功!
小红取钱成功,吐出了100000.0元成功!
小明取钱成功后,当前余额剩余0.0元
小红取钱成功后,当前余额剩余-100000.0元
可见此时账户余额为-10万,银行血亏10万元,这就是线程不安全导致的问题。
3、总结
以上就是Thread类的常用方法、线程安全问题,如果想了解更多的线程知识,请关注本博主~~