文章目录
1、多线程基础
1.1、线程相关概念
1.1.1、程序(program)
1.1.2、进程
1.1.3、线程
1.1.4、其他相关概念
1.2、线程基本使用
1.2.1、创建线程的两种方式
1.2.2、线程应用方式1 - 继承 Thread 类
package threaduse;
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
// 创建 Cat 对象, 可以当做线程使用
Cat cat = new Cat();
// 源码
/**
(1)
public synchronized void start() {
start0();
}
(2)
start0() 是本地方法, 是 JVM 调用, 底层是 c/c++实现
真正实现多线程的效果, 是 start0(), 而不是 run
private native void start0();
*/
cat.start(); // 启动线程 -> 最终会执行 cat 的 run 方法
// cat.run();
// run 方法就是一个普通的方法, 没有真正的启动一个线程, 就会把 run 方法执行完毕, 才向下执行
// 说明: 当 main 线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行
// 这时 主线程和子线程是交替执行
System.out.println("主线程继续执行" + Thread.currentThread().getName()); // 名字 main
for (int i = 0; i < 60; i++) {
System.out.println("主线程i=" + i);
// 让主线程休眠
Thread.sleep(1000);
}
}
}
// 1. 当一个类继承了 Thread 类, 该类就可以当做线程使用
// 2. 我们会重写 run 方法, 写上自己的业务代码
// 3. run Thread 类 实现了 Runnable 接口的 run 方法
/*
@Override
public void run() {
if (target != null) {
target.run();
}
}
*/
class Cat extends Thread {
int times = 0;
@Override
public void run() { // 重写 run 方法, 写上自己的业务逻辑
while (true) {
// 该线程每隔 1 秒, 在控制台输出 “喵喵, 我是小猫咪”
System.out.println("喵喵, 我是小猫咪 " + (++times) + " 线程名=" + Thread.currentThread().getName());
// 让该线程休眠 1 秒 ctrl+alt+t
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times == 80) break; // 当 times 到 80, 退出 while, 这时线程也就退出
}
}
}
1.2.3、线程应用2 - 实现 Runnable 接口
package threaduse;
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
// dog.start(); 这里不能调用 start
// 创建了 Thread 对象, 把 dog 对象(实现 Runnable), 放入 Thread
Thread thread = new Thread(dog);
thread.start();
Tiger tiger = new Tiger();
ThreadProxy threadProxy = new ThreadProxy(tiger);
threadProxy.start();
}
}
class Animal {
}
class Tiger extends Animal implements Runnable {
@Override
public void run() {
System.out.println("老虎嗷嗷叫...");
}
}
// 线程代理类, 模拟了一个极简的 Thread 类
class ThreadProxy implements Runnable { // 你可以把 Proxy 类当做 ThreadProxy
private Runnable target = null; // 属性, 类型是 Runnable
@Override
public void run() {
if (target != null) {
target.run(); // 动态绑定(运行类型 Tiger)
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start() {
start0(); // 这个方法时真正实现多线程方法
}
public void start0() {
run();
}
}
class Dog implements Runnable { // 通过实现 Runnable 接口, 开发线程
int count = 0;
@Override
public void run() { // 普通方法
while (true) {
System.out.println("小狗汪汪叫..hi " + (++count) + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) break;
}
}
}
1.2.4、线程使用应用案例 - 多线程执行
package threaduse;
public class Thread03 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
}
}
class T1 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
// 每隔 1 秒输出 “hello,world”, 输出 10次
System.out.println("hello,world " + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) break;
}
}
}
class T2 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
// 每隔 1 秒输出 “hi”, 输出 5 次
System.out.println("hi " + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 5) break;
}
}
}
1.2.5、线程如何理解
1.2.6、继承 Thread vs 实现 Runnable 的区别
package threaduse;
/**
* 使用多线程, 模拟三个窗口同时售票 100 张
*/
public class SellTicket {
public static void main(String[] args) {
// 测试
System.out.println("=== 继承Thread方式来售票 ===");
SellTicket01 sellTicket01 = new SellTicket01();
SellTicket01 sellTicket02 = new SellTicket01();
SellTicket01 sellTicket03 = new SellTicket01();
// 这里我们会出现超卖
sellTicket01.start(); // 启动售票线程
sellTicket02.start(); // 启动售票线程
sellTicket03.start(); // 启动售票线程
System.out.println("=== 使用实现接口方式来售票 ===");
SellTicket02 sellTicket021 = new SellTicket02();
new Thread(sellTicket021).start(); // 第 1 个线程-窗口
new Thread(sellTicket021).start(); // 第 2 个线程-窗口
new Thread(sellTicket021).start(); // 第 3 个线程-窗口
// 结论: 这两种方式都会出现超卖的现象
}
}
class SellTicket01 extends Thread {
private static int ticketNum = 100; // 让多个线程共享 ticketNum
@Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("售票结束...");
break;
}
// 休眠50毫秒, 模拟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));
}
}
}
// 实现接口方式
class SellTicket02 implements Runnable {
private int ticketNum = 100; // 让多个线程共享 ticketNum
@Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("售票结束...");
break;
}
// 休眠50毫秒, 模拟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));
}
}
}
结论:这两种方式都会出现超卖的现象