第14章 多线程基础
14.1 线程相关概念
14.1.1 程序(program)
是为完成特定任务,用某种语言编写的一组指令的集合,即编写的代码。
示例代码(以Java为例):- public class BallMove extends JFrame {
- MyPanel mp = null;
- public static void main(String[] args) {
- BallMove ballMove = new BallMove();
- }
- public BallMove() {
- mp = new MyPanel();
- this.add(mp);
- this.setSize(400, 300);
- this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- this.setVisible(true);
- }
- }
复制代码 14.1.2 进程
- 运行中的程序,如启动QQ、迅雷,操作系统会为其分配内存空间。
- 是程序的一次执行过程,有产生、存在和消亡的动态过程 。
14.1.3 线程
- 由进程创建,是进程的实体,一个进程可拥有多个线程 。
- 举例:后续会将多线程加入“坦克大战”游戏实践。
14.1.4 其他相关概念
- 单线程:同一时刻仅允许执行一个线程。
- 多线程:同一时刻可执行多个线程,如QQ同时开多个聊天窗口、迅雷同时下载多个文件 。
- 并发:同一时刻多个任务交替执行,单CPU实现“貌似同时” 。
- 并行:同一时刻多个任务真正同时执行,多核CPU可实现,Java支持。
14.2 线程基本使用
14.2.1 创建线程的两种方式(Java)
- 继承Thread类:重写run方法,定义线程执行逻辑。
- 实现Runnable接口:重写run方法,解决Java单继承局限,更灵活。
14.2.2 应用案例1 - 继承Thread类
- package com.ming.threaduse;
- /**
- * @author 明
- * @version 1.0
- * 演示通过继承Thread 类创建线程
- */
- 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.run();//run方法就是一个普通的方法, 没有真正的启动一个线程,就会把run方法执行完毕,才向下执行
- //说明: 当main线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行
- //这时 主线程和子线程是交替执行..
- cat.start();//启动线程-> 最终会执行cat的run方法
- System.out.println("主线程继续执行" + Thread.currentThread().getName());
- for (int i = 0; i < 10; 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) {
- System.out.println("喵喵,我是小猫咪"+times++ + Thread.currentThread().getName());
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if(times==8){
- break;
- }
- }
- }
- }
复制代码
14.2.3 应用案例2 - 实现Runnable接口
需求:每隔1秒在控制台输出“hi!”,输出10次后退出。
实现:- package com.ming.threaduse;
- import java.util.concurrent.Callable;
- import java.util.concurrent.FutureTask;
- /**
- * @author 明
- * @version 1.0
- * 通过实现接口Runnable 来开发线程
- */
- public class Thread02 {
- public static void main(String[] args) throws InterruptedException {
- // 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 {
- int count = 0;
- @Override
- public void run() {
- while (true) {
- System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());
- //休眠1秒
- try {
- Thread.sleep(1000);
- }catch (Exception e){
- e.printStackTrace();
- }
- if(count == 10){
- break;
- }
- }
- }
- }
复制代码 14..2.4线程使用应用案例-多线程执行
- package com.ming.threaduse;
- /**
- * @author 明
- * @version 1.0
- * main线程启动两个子线程
- */
- 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) {
- 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) {
- System.out.println("hi " + (++count));
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (count == 5){
- break;
- }
- }
- }
- }
复制代码
14.3 继承Thread vs 实现Runnable的区别
- 设计角度:Thread类本身实现Runnable接口,本质无区别,但实现接口更灵活。
- 适用场景:实现Runnable适合多线程共享资源,且避免单继承限制,推荐优先使用。
- 案例:售票系统
模拟三个售票窗口售100张票,对比两种方式,分析线程安全等问题(如资源竞争导致超卖,后续需同步机制解决 )。
[code]package com.ming.ticket;import com.hspedu.ticket.SellTicket;/** * @author 明 * @version 1.0 * 使用多线程,模拟三个窗口同时售票100张 */public class SellTIcket { public static void main(String[] args) { //测试// SellTicket01 sellTicket01 = new SellTicket01();// SellTicket01 sellTicket02 = new SellTicket01();// SellTicket01 sellTicket03 = new SellTicket01();//// //这里我们会出现超卖..// sellTicket01.start();//启动售票线程// sellTicket02.start();//启动售票线程// sellTicket03.start();//启动售票线程 System.out.println("===使用实现接口方式来售票====="); SellTicket02 sellTicket02 = new SellTicket02(); new Thread(sellTicket02).start(); new Thread(sellTicket02).start(); new Thread(sellTicket02).start(); }}//使用Thread方式class SellTicket01 extends Thread { private static int ticketNum = 100;//让多个线程共享ticketNum @Override public void run() { while (true){ if (ticketNum |