线程创建,线程通信,线程安全
单线程
线程是一个内部程序的一条执行路径。
我们之前执行的main路程就是一条执行线程
程序中只有一条执行的线程就是单线程的。多线程,反之亦然
多线程创建方式
多线程的创建的方式:
- 继承自Thread
- 实现Runnable接口
- Jdk 5.0 增加了:实现Callable接口
- 定义一个子类继承自Thread,重写run() 方法,定义线程任务
- 创建子类对象
- 调用对象的start()方法,启动线程(启动后还是执行run()方法的代码)
为什么不直接调用run()方法要调用start()方法?
直接调用run方法会变成普通方法,此时相当于还是单线程
只有调用start()方法才是启动一个新的线程
不能把主线程放在子线程前面这样相当于一个单线程的效果,这样会优先执行主线程
实现接口
继承实现多线程
实现线程步骤
- 在run()方法中编写线程任务,继承线程类
- 在测试类中先创建线程任务类的对象,将该对象给线程对象
- 创建线程对象
调用start() 启动线程
package com.pan.crizab;
//缺点较多,不便于扩展接口任务对象的功能
public class ThreadExtend extends Thread {public void run(){
System.out.println("通过继承的方式实现线程的任务");
}
}
class Main{public static void main(String[] args) {
//1.创建线程任务对象
ThreadExtend threadExtend = new ThreadExtend();
//2.创建线程对象
Thread thread1 = new Thread(threadExtend);
thread1.start();
}
}
缺点:不便于扩展,不能处理结果集
实现runnable接口方式
实现线程步骤
- 实现接口Runnable,在run()方法中编写线程任务
- 在测试类中先创建线程任务类的对象,将该对象给线程对象
- 创建线程对象
调用start() 启动线程
package com.pan.crizab;
//便于扩展,但是不能处理返回要求
public class RunnableImp extends Thread implements Runnable{@Override
public void run() {
System.out.println("通过实现runnable接口实现线程任务");
}
}
class Main1{
public static void main(String[] args) {
//1.多态式创建接口实现类线程任务对象
Runnable runnable1 = new RunnableImp();
//2.创建线程,将线程任务对象传入线程
Thread thread1 = new Thread(runnable1);
//3.启动线程
thread1.start();
}
}
缺点:不能处理线程任务的返回值,没有结果返回
优点:扩展性强,在实现接口的同时可以继承其他的类,完成一些方法的重写
实现Callable函数式接口
Callable类有
实现线程步骤
- 实现callable接口,在run()方法中编写线程任务
将线程任务对象给FutureTask对象进行修饰包装
- FutureTask类是继承自runnable类,也就是其本质还是Runnable接口只是实现了返回结果的处理
- 在测试类中先创建线程任务类的对象,将给对象给线程对象
- 创建线程对象
- 调用start() 启动线程
使用futureTask对象调用get获取结果返回
package com.pan.crizab;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class CallableImp {
,
public static void main(String[] args) {
//创建线程任务对象
Callable callable = new CallableIm(100);
//封装该对象,使之可以在线程对象中入参,以便后面调用get()方法获取返回值
FutureTask<Object> f1 = new FutureTask<Object>(callable);
Thread t1 = new Thread(f1);
t1.start();
try {
int s = (Integer) f1.get();
System.out.println("f1执行后的结果"+s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//创建线程任务对象
Callable callable2 = new CallableIm(100);
//封装该对象,使之可以在线程对象中入参,以便后面调用get()方法获取返回值
FutureTask<Object> f2 = new FutureTask<Object>(callable);
Thread t2 = new Thread(f2);
t2.start();
try {
int s = (Integer) f2.get();
System.out.println("f2执行后的结果"+s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class CallableIm implements Callable<Object>{
private int n;
public CallableIm(int n) {
this.n = n;
}
@Override
public Object call() throws Exception {
int sum = 0;
for(int i = 0;i<n;i++){
sum += i;
}
System.out.println("线程执行任务");
return sum;
}
}
缺点:代码量过多
优点:便于扩展,便于处理结果的返回值,
线程常用API
区分各个线程:setName(String name),getName();
sleep();
wait()
notifyAll();
在许多的线程中,创建当前的线程对象
线程安全问题
多个线程同时操作一个共享资源的时候可能会出现业务安全问题,称为线程安全问题
详解:
银行取钱:
小红与小白拥有一个共同账户,余额10万元
小红和小白同时取钱10万元
线程并发执行,此时都判断都为可以取出,则同时取出10万元
出现余额为-10万余元的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iVZn04CW-1672539577713)(D:\HONOR Magic-link\Screenshot\capture_20221230210642785.bmp)]
解决办法:也就是加锁,对资源区加锁
synchronized
代码块形式
其中 this 就是账户类的对象,用于指定访问时的线程
synchronized (this){
//代码
}
方法修饰形式
//同步方法锁
public synchronized void drawMoney(double money){
//获取用户的Id()
String name = Thread.currentThread().getName();
//lock.lock();
if(this.money >= money){
System.out.println(""+name+"来取钱,取出"+money );
this.money -= money;
Thread.sleep(5000);
System.out.println("余额:"+ this.money);
}
else{
System.out.println(name+"来取钱"+money);
System.out.println("余额不足");
}
//lock.unlock();
}
Lock:
//防止中途出现异常,迟迟不释放资源区
所以当出现异常时,也要执行完 unlock();
lock();
unlock();
finally{
lock1.unlock();
}
账户类
package com.pan.items;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
private String carId;
private Double money;
//创建Lock锁的实现类对象
private final Lock lock = new ReentrantLock();
public Account(String carId, Double money) {
this.carId = carId;
this.money = money;
}
//同步方法锁
public synchronized void drawMoney(double money){
//获取用户的Id()
String name = Thread.currentThread().getName();
// lock.lock();
//存钱
if(this.money >= money){
System.out.println(""+name+"来取钱,取出"+money );
this.money -= money;
System.out.println("余额:"+ this.money);
}
else{
System.out.println(name+"来取钱"+money);
System.out.println("余额不足");
}
//lock.unlock();
}
public String getCarId() {
return carId;
}
public void setCarId(String carId) {
this.carId = carId;
}java
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
}
线程任务类:
package com.pan.items;
public class RunnableDemo extends Thread implements Runnable {
private Account account;
public RunnableDemo(String name, Account account) {
super(name);
this.account = account;
}
@Override
public void run() {
account.drawMoney(100000.0);
}
}
执行测试类:
package com.pan.items;
public class Main1 {
public static void main(String[] args) {
Account account = new Account("123ID",100000.0);
new RunnableDemo("小红",account).start();
new RunnableDemo("小白",account).start();
}
}
结果如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l6FsgKuk-1672539577716)(C:\Users\abundan\AppData\Roaming\Typora\typora-user-images\image-20221230210537496.png)]
线程通信
详解
线程通信就是各个线程之间共享信息,操作同一个资源区,各个线程可以接收来自不同线程所共享的数据,根据数据信息抢占资源或者暂停资源的夺取。
eg:
五个人共享一个账户,账户:资源区。五个人:五个线程
三人负责存钱,两人负责取钱
但同一时间只能有一个人可以操作该账户,所以抢到资源后就要停止他们的争夺,就会用到通信的API
wait(),notifyAll(),
如下代码实现
public synchronized void drawMoney(double money) throws Exception {
//获取用户的Id()
String name = Thread.currentThread().getName();
// lock.lock();
if(this.money >= money){
System.out.println(""+name+"来取钱,取出"+money );
this.money -= money;
System.out.println("余额:"+ this.money);
this.notifyAll();
this.wait();
}
else{
System.out.println(name+"来取钱"+money);
System.out.println("余额不足,通知存钱");
this.notifyAll();
this.wait();
}
//lock.unlock();
}
public synchronized void saveMoney(double v) throws Exception {
String name = Thread.currentThread().getName();
if (this.money == 0){
System.out.println(name+"来存钱同时通知其他线程取钱,存钱数量:"+v);
this.money += v;
Thread.sleep(5000);
this.notifyAll();
this.wait();
}else {
System.out.println(name+"查询=====================还有钱,余额:"+money);
this.notifyAll();
this.wait();
}
}
还没有评论,来说两句吧...