多线程的创建
方式一:继承Thread类
public class Test {
public static void main(String[] args) {
// 3. new一个线程对象
Thread t = new MyThread();
// 4. 调用start方法启动线程(执行的还是run)
t.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行输出:" + i);
}
}
}
/*
1. 定义一个线程类继承Thread类
*/
class MyThread extends Thread {
/**
2. 重写run方法,里面定义线程以后要干啥
*/
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程输出第:" + i + "次");
}
}
}
方式二:实现Runnable接口
public class Test {
public static void main(String[] args) {
//3. 创建一个任务对象
Runnable target = new MyThread();
//4. 把任务对象交给Thread处理
Thread t = new Thread(target);
//5. 启动线程
t.start();
for (int i = 0; i < 500; i++) {
System.out.println("主线程执行输出: " + i);
}
}
}
/*
1. 定义一个线程任务类 实现Runnable接口
*/
class MyThread implements Runnable{
/**
* 2. 重写run方法,定义线程的执行任务的
*/
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("子线程执行输出: " + i);
}
}
}
/*
匿名内部类实现
*/
public class Test {
public static void main(String[] args) {
new Thread(() ->{
for (int i = 0; i < 500; i++)
System.out.println("子线程执行输出: " + i);
}).start();
for (int i = 0; i < 500; i++) {
System.out.println("主线程执行输出: " + i);
}
}
}
方式三:JDK5.0新增:实现Callable接
public class Test {
public static void main(String[] args) {
// 3. 创建Callable对象
Callable<String> call = new MyCallable(100);
// 4.把Callable任务对象交给FutureTask对象
// FutureTask对象的作用1:
// 是Runnable的对象(实现了Runnable接口),可以交给Thread了
// FutureTask对象的作用2:
// 可以在线程执行完毕之后通过调用其get方法得到线程执行完成的结果
FutureTask<String> ft = new FutureTask<String>(call);
// 5. 交给线程处理
Thread t1 = new Thread(ft);
// 6. 启动线程
t1.start();
try {
String rs1 = ft.get();
System.out.println("第一个结果:" + rs1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
1. 定义一个任务类实现Callable接口
应该声明线程任务执行完毕后结果的数据类型
*/
class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
/*
2. 重写call方法(线程的任务方法)
*/
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += i;
}
return "子线程的累加和为:" + sum;
}
}
总结
Thread的常用方法
public class Test {
public static void main(String[] args) {
Thread t1 = new MyCallable("一号");
t1.start();
System.out.println(t1.getName());
Thread t2 = new MyCallable("二号");
t2.start();
System.out.println(t2.getName());
// 哪个线程执行它,它就得到哪个线程
// 主线程名称就叫main
Thread m = Thread.currentThread();
System.out.println(m.getName());
for (int i = 0; i < 5; i++) {
System.out.println("main线程输出" + i);
}
}
}
/**
1. 定义一个任务类实现Callable接口
应该声明线程任务执行完毕后结果的数据类型
*/
class MyCallable extends Thread {
public MyCallable() {
}
// 为当前线程对象设置名称,送给父类的有参构造函数
public MyCallable(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "子线程" + i);
}
}
}
Thread.sleep(5000);
线程安全
线程安全问题是什么、发生的原因
线程安全问题案例模拟
public class Account {
private String cardId;
private double money; //账户余额
public Account(String cardId ,double money) {
this.cardId = cardId;
this.money = money;
}
/**
开始取钱
*/
public void drawMoney(double money) {
// 1.获取是谁来取钱
String name = Thread.currentThread().getName();
// 判断账户是否有钱
if (this.money >= money) {
// 2.取钱
System.out.println(name + "取钱成功");
System.out.println("取出 " + money + "元");
System.out.println();
// 3.更新余额
this.money -= money;
System.out.println("当前余额为:" + this.money);
}else {
System.out.println(name + " 余额不足");
System.out.println();
}
}
}
/**
取钱的线程类
*/
public class DrawThread extends Thread {
// 接收处理账户对象
private Account acc;
public DrawThread(Account acc ,String name) {
super(name);
this.acc =acc;
}
@Override
public void run() {
// 小明、小红:取钱
acc.drawMoney(100000.0);
}
}
public class Test {
public static void main(String[] args) {
// 1. 定义线程类,创建一个账户对象
Account acc = new Account("001",100000.0);
// 2. 创建两个线程对象代表两个人同时进来了
new DrawThread(acc ,"小明").start();
new DrawThread(acc ,"小红").start();
}
}
结果
小明取钱成功
小红取钱成功
取出 100000.0元
取出 100000.0元
当前余额为:-100000.0
当前余额为:0.0
线程同步
同步思想概述
方式一:同步代码块
public void drawMoney(double money) {
// 1.获取是谁来取钱
String name = Thread.currentThread().getName();
// 同步代码块
// this == acc的共享账户
synchronized (this) {
// 判断账户是否有钱
if (this.money >= money) {
// 2.取钱
System.out.println(name + "取钱成功");
System.out.println("取出 " + money + "元");
System.out.println();
// 3.更新余额
this.money -= money;
System.out.println("当前余额为:" + this.money);
}else {
System.out.println(name + " 余额不足");
System.out.println();
}
}
}
}
方式二:同步方法
/**
开始取钱
*/
public synchronized void drawMoney(double money) {
// 1.获取是谁来取钱
String name = Thread.currentThread().getName();
// 判断账户是否有钱
if (this.money >= money) {
// 2.取钱
System.out.println(name + "取钱成功");
System.out.println("取出 " + money + "元");
System.out.println();
// 3.更新余额
this.money -= money;
System.out.println("当前余额为:" + this.money);
} else {
System.out.println(name + " 余额不足");
System.out.println();
}
}
}
方式三:Lock锁
/**
开始取钱
*/
// final修饰的成员变量唯一且不可被替换
private final Lock lock = new ReentrantLock();
public void drawMoney(double money) {
// 1.获取是谁来取钱
String name = Thread.currentThread().getName();
// 判断账户是否有钱
lock.lock(); // 上锁
try {
if (this.money >= money) {
// 2.取钱
System.out.println(name + "取钱成功");
System.out.println("取出 " + money + "元");
System.out.println();
// 3.更新余额
this.money -= money;
System.out.println("当前余额为:" + this.money);
} else {
System.out.println(name + " 余额不足");
System.out.println();
}
} finally {
lock.unlock(); // 解锁
}
}
}
线程通信[了解]
public class Phone{
// 实现线程间通信,默认为手机当前处于等待来电提醒
private boolean flag = false;
public void run() {
// a. 负责来电提醒的线程
new Thread(() -> {
try {
while (true) {
synchronized (Phone.this) {
if (!flag) {
// 代表要来电提醒了
System.out.println("有新电话接入,请接听");
flag = true;
Phone.this.notify();
Phone.this.wait();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
// b. 接电话线程,正式接通了
new Thread(() -> {
try {
while (true) {
synchronized (Phone.this) {
if (flag) {
// 可以接通电话了
System.out.println("通话结束");
Thread.sleep(1000);
flag = false; // 代表继续等待呼入电话
//唤醒别人,等待自己
Phone.this.notify();
Phone.this.wait();
}else {
Phone.this.notify();
Phone.this.wait();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
public static void main(String[] args) {
// 1. 创建一部手机对象
Phone huawei = new Phone();
huawei.run(); // 开机了
}
}
线程池[重点]
线程池概述
线程池实现的API、参数说明
线程池处理Runnable任务
线程池处理Callable任务
Executors工具类实现线程池
public class Test {
public static void main(String[] args) {
// 1. 创建固定线程数据的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());
pool.execute(new MyRunnable()); // 已经没有多余的线程了
}
}
定时器
public class Time {
public static void main(String[] args) {
// 1. 创建Timer定时器
Timer timer = new Timer(); // 定时器本身就是一个单线程
// 2. 调用方法处理定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行一次");
}
},3000,2000);
}
}
补充知识:生命周期、并发并行