## 多线程 ### 一、进程与线程的概念 进程: 进程是应用程序的执行实例,有独立的内存空间,占用独立的系统资源。 线程:包含在进程之内的,有主线程与子线程的区分。是CPU调度和分派的基本单位,每个子线程都可以独立的完成一个功能。 什么是多线程? 如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”,多个线程交替占用CPU资源,而非真正的并行执行 多线程好处:充分利用CPU的资源,简化编程模型,带来良好的用户体验 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU) 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。 ### 二、主线程 **Thread类** Java提供了java.lang.Thread类支持多线程编程 **主线程**: main()方法即为主线程入口 产生其他子线程的线程 必须最后完成执行,因为它执行各种关闭动作 ~~~java public static void main(String[] args) { Thread thread = Thread.currentThread(); //线程对象 String name = thread.getName(); System.out.println("线程名称为:"+name); //主线程 thread.setName("mythread"); System.out.println("线程名称为:"+thread.getName()); } ~~~ ### 三、创建线程 在Java中创建线程的三种方式: - 继承java.lang.Thread类 - 实现java.lang.Runnable接口 - 实现Callable接口和Future接口 使用线程的步骤: 1、定义线程 2、创建线程对象 3、启动线程 4、终止线程 #### 3.1 继承Thread类实现创建线程 步骤: - 定义MyThread类继承Thread类 - 重写run()方法,编写线程执行体 - 创建线程对象,调用start()方法启动线程 示例 ~~~java public class MyThread extends Thread { @Override public void run() { String name = Thread.currentThread().getName(); switch (name){ case "xc1": for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } break; case "xc2": for (int i = 0; i < 10; i++) { System.out.println("好好学习java"+i); } break; } } } ~~~ 创建线程对象: ~~~java public static void main(String[] args) { MyThread myThread1=new MyThread(); //创建线程对象 myThread1.setName("xc1"); myThread1.start(); //启动线程 MyThread myThread2=new MyThread(); //创建线程对象 myThread2.setName("xc2"); myThread2.start(); //启动线程 } ~~~ **多个线程交替执行,不是真正的“并行”,线程每次执行时长由分配的CPU时间片长度决定** #### 3.2 实现Runnbale接口创建线程 步骤: - 定义MyRunnable类实现Runnable接口 - 实现run()方法,编写线程执行体 - 创建线程对象,调用start()方法启动线程 示例 ~~~java public class MyRunable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } ~~~ 创建线程对象 ~~~java public class Test1 { public static void main(String[] args) { Runnable myRunable=new MyRunable(); //1、实例化对象 Thread thread=new Thread(myRunable); //2、构建一个线程对象 thread.setName("线程1"); thread.start(); //3、启动线程 Thread thread1=new Thread(myRunable); thread1.setName("线程2"); thread1.start(); } } ~~~ #### 3.3 实现Callable接口和Future接口 定义线程 步骤: 1、创建Callable接口的实现类,并重写call()方法,该方法具有返回值 2、实例化实现类对象,通过实现类对象进行入参来构建一个FutureTask对象,FutureTask对象实现了Runable接口,Future也是Runable和Future接口的实现类 3、通过FutureTask对象入参构建一个线程对象,并调用start()方法来启动执行线程 4、适用FutureTask对象的get()方法来获得线程执行的结果 ~~~java import java.util.concurrent.Callable; /** @author:mengshujun @createTime: 2024-03-20 10:38:41 星期三 */ public class MyThread3 implements Callable<Integer> { @Override public Integer call() throws Exception { int num1=11; int num2=22; int sum=num1+num2; return sum; } } ~~~ 创建线程并调用 ~~~java public class Test2 { public static void main(String[] args) { MyThread3 m3=new MyThread3(); //实例化对象 FutureTask<Integer> futureTask=new FutureTask(m3); //Runable对象 //创建线程 Thread t1=new Thread(futureTask); t1.setName("线程一"); t1.start(); //new Thread(futureTask,"线程一").start(); try { System.out.println("线程的执行结果为:"+futureTask.get()); //获得线程的返回值 } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } ~~~ 适用于线程有返回值的情况,可以适用这种方式来创建线程,接收结果。 三种创建线程的区别: 1、继承Thread类,编写简单,可直接操作线程,适用于单继承 2、实现Runnable接口,避免单继承局限性,便于共享资源。 3、实现Callable接口,可以接收线程运行的结果。 **推荐使用实现Runnable接口方式创建线程** **问题:启动线程是否可以直接调用run()方法?** 如果直接调用run(),只有主线程一条执行路径。调用start(),有多条执行路径,主线程和子线程并行交替执行 练习1: ~~~ 需求说明 创建两个子线程,每个线程均输出20次消息数字、“你好”、线程名 观察多个线程交替执行的过程 ~~~ ### 四、线程的五种状态 ![1](D:/移动文件/1.png) 创建类 ~~~java package com; public class MyRunnable implements Runnable { @Override public void run() { String name = Thread.currentThread().getName(); System.out.println("线程" + name + ",现在处于就绪状态"); for (int i = 0; i < 30; i++) { System.out.println("线程" + name + ",现在处于运行状态"); try { Thread.sleep(3); System.out.println("线程" + name + ",现在处于阻塞状态"); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程" + name + ",现在处于死亡状态"); } } ~~~ 创建线程 ~~~java public static void main(String[] args) { MyRunnable myRunnable=new MyRunnable(); Thread thread=new Thread(myRunnable); thread.setName("线程一"); System.out.println("线程"+thread.getName()+",现在处于创建状态"); thread.start(); } ~~~ ### 五、线程调度 线程调度指按照特定机制为多个线程分配CPU的使用权 ![2](D:/移动文件/2.png) 线程优先级:线程优先级由1~10表示,1最低,默认优先级为5,优先级高的线程获得CPU资源的概率较大 线程休眠:让线程暂时睡眠指定时长,线程进入阻塞状态,睡眠时间过后线程会再进入可运行状态 线程礼让:暂停当前线程,允许其他具有相同优先级的线程获得运行机会,该线程处于就绪状态,不转为阻塞状态 线程中断:使当前线程暂停执行,等待其他线程结束后再继续执行本线程 ~~~java public final void join() public final void join(long mills) public final void join(long mills,int nanos) ~~~ millis:以毫秒为单位的等待时长 nanos:要等待的附加纳秒时长 需处理InterruptedException异常 ### 六、线程使用同一资源的数据安全问题 存在问题的代码 ~~~java package net; /** * 抢票线程类 */ public class BuyThread implements Runnable { public int num = 10; //总票数 public int count = 0; //记录当前的用户(线程)抢到了第几张票 @Override public void run() { while (true) { if (num == 0) { System.out.println("票已被抢完,抢票活动结束!"); break; } num--; count++; System.out.println("当前用户(线程)," + Thread.currentThread().getName() + "抢到了第" + count + "张票,总票数还剩余" + num + "张"); } } } ~~~ 创建线程类 ~~~java public static void main(String[] args) { BuyThread buyThread = new BuyThread(); Thread t1 = new Thread(buyThread, "徐磊"); Thread t2 = new Thread(buyThread, "贾恒广"); Thread t3 = new Thread(buyThread, "袁晓波"); t1.start(); t2.start(); t3.start(); } ~~~ ### 七、解决线程安全问题有两种解决方案 #### 7.1、使用synchronized (this) 代码块来解决 同步代码块锁 ,this就代表的是当前进来的线程对象,进行锁定,本线程执行完操作后才会释放锁。 ~~~java package net; /** * 抢票线程类 */ public class BuyThread implements Runnable { public int num = 10; //总票数 public int count = 0; //记录当前的用户(线程)抢到了第几张票 @Override public void run() { //同步代码块锁 ,this就代表的是当前进来的线程对象 //1 while (true) { synchronized (this) { if (num == 0) { System.out.println("票已被抢完,抢票活动结束!"); break; } num--; count++; System.out.println("当前用户(线程)," + Thread.currentThread().getName() + "抢到了第" + count + "张票,总票数还剩余" + num + "张"); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } ~~~ #### 7.2、定义synchronized方法来解决 ~~~java package net; /** * 抢票线程类 */ public class BuyThread implements Runnable { public int num = 10; //总票数 public int count = 0; //记录当前的用户(线程)抢到了第几张票 @Override public void run() { while (true) { if (!cale()) { break; } } } //同步方法 public synchronized boolean cale() { boolean isFlag = true; if (num == 0) { isFlag = false; System.out.println("票已售完!"); return isFlag; } num--; count++; System.out.println("当前用户(线程)," + Thread.currentThread().getName() + "抢到了第" + count + "张票,总票数还剩余" + num + "张"); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return isFlag; } } ~~~ ### 八、线程安全的类型 ~~~java ArrayList list=new ArrayList(); //动态的数组 list.add(1); Vector vector=new Vector(); //线程安全 vector.add(100); ~~~ ~~~java HashMap map1=new HashMap(); map1.put("id","111"); Hashtable table1=new Hashtable(); //线程安全 table1.put("id","222"); ~~~ ~~~java StringBuilder sb1=new StringBuilder(); sb1.append("ddd"); StringBuffer sb=new StringBuffer(); //线程安全 sb.append("abc"); ~~~
java多线程操作
于 2024-07-13 15:50:02 首次发布