什么是多线程

什么是多线程什么是线程 线程是一个程序内部的一条执行流程程序中如果只有一条执行流程 那这个程序是单线程的程序什么是多线程 多线程是从软硬件上实现的多条执行流程的技术 多条线程由 cpu 负责调度执行 多线程程序是什么意思

大家好,欢迎来到IT知识分享网。

什么是线程?

线程是一个程序内部的一条执行流程

程序中如果只有一条执行流程,那这个程序是单线程的程序

什么是多线程?

多线程是从软硬件上实现的多条执行流程的技术(多条线程由cpu负责调度执行)

多线程创建

多线程的创建方式一:继承Thread类

定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
创建MyThread类的对象
调用线程对象的start()方法启动线程(启动后还是执行run方法的)

优缺点:
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。

直接调用run方法会当成普通方法执行,此时相当于还是单线程执行
只有调用start方法才是启动一个新的线程执行
2、不要把主线程任务放在启动子线程之前

这样主线程一直是先跑完的,相当于是一个单线程的效果了

什么是多线程

package com.cqh.demo01; / * @author cqh * @date 2024/1/16 * @Description */ // 1、子类继承Thread线程类 public class MyThread extends Thread{ // 重写Thread类的 run方法 @Override public void run() { // 描述线程执行任务 for (int i = 0; i <=5 ; i++) { System.out.println("子线程= " + i); } } } 

package com.cqh.demo01; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadTest01 { // main方法是由一条默认的主线程执行 public static void main(String[] args) { // 3、创建一个MyThread线程类的对象代表一个线程 Thread myThread = new MyThread(); // 启动线程 myThread.start(); for (int i = 0; i <=5 ; i++) { System.out.println("主线程 = " + i); } } } 

什么是多线程

多线程的创建方式二:实现Runnable接口

定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
创建MyRunnable任务对象
把MyRunnable任务对象交给Thread处理 

什么是多线程

调用线程对象的start()方法启动线程

优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。

缺点:需要多一个Runnable对象。

什么是多线程

package com.cqh.demo02; / * @author cqh * @date 2024/1/16 * @Description */ // 1、定义一个类实现runnable接口 public class MyRunnable implements Runnable{ // 2、重写run方法 @Override public void run() { // 线程执行的任务 for (int i = 0; i <=5 ; i++) { System.out.println("子线程= " + i); } } } 
package com.cqh.demo02; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadTest01 { // main方法是由一条默认的主线程执行 public static void main(String[] args) { // 3、创建一个MyRunnable线程类的对象代表一个线程 Runnable myRunnable = new MyRunnable(); // 4、把任务对象交给线程对象处理 // 启动线程 new Thread(myRunnable).start(); for (int i = 0; i <=5 ; i++) { System.out.println("主线程 = " + i); } } } 

 什么是多线程

 线程创建方式二的匿名 内部类 写法

可以创建Runnable的匿名内部类对象。
再交给Thread线程对象。
再调用线程对象的start()启动线程。

什么是多线程

package com.cqh.demo03; import com.cqh.demo01.MyThread; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadTest01 { // main方法是由一条默认的主线程执行 public static void main(String[] args) { // 1、直接创建Runnable接口的匿名内部类形式 Runnable runnable = new Runnable(){ @Override public void run() { for (int i = 0; i <=5 ; i++) { System.out.println("子线程 = " + i); } } }; // 2、把任务对象交给线程对象处理 // 启动线程 new Thread(runnable).start(); for (int i = 0; i <=5 ; i++) { System.out.println("主线程 = " + i); } } } 

 什么是多线程

简化
package com.cqh.demo03; import com.cqh.demo01.MyThread; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadTest01 { // main方法是由一条默认的主线程执行 public static void main(String[] args) { // 1、直接创建Runnable接口的匿名内部类形式 Runnable runnable = new Runnable(){ @Override public void run() { for (int i = 0; i <=5 ; i++) { System.out.println("子线程1 = " + i); } } }; // 2、把任务对象交给线程对象处理 // 启动线程 new Thread(runnable).start(); // 简化1 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i <=5 ; i++) { System.out.println("子线程2 = " + i); } } }).start(); // 简化2 new Thread(()-> { for (int i = 0; i <=5 ; i++) { System.out.println("子线程3 = " + i); } }).start(); for (int i = 0; i <=5 ; i++) { System.out.println("主线程 = " + i); } } } 

 前两种线程创建方式都存在的一个问题
假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果。

解决

JDK50提供了Callable接口和FutureTask类来实现(多线程的第三种创建方式)

多线程的第三种创建方式:利用Callable接口、FutureTask类来实现

1、创建任务对象

2、把线程任务对象交给Thread对象。

什么是多线程

package com.cqh.demo04; import java.util.concurrent.Callable; / * @author cqh * @date 2024/1/16 * @Description */ // 创建一个类实现Callable接口 public 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 "线程求出了1-"+n+"的和为"+sum; } } 

package com.cqh.demo04; import com.cqh.demo02.MyRunnable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadTest01 { // main方法是由一条默认的主线程执行 public static void main(String[] args) throws ExecutionException, InterruptedException { // 3、创建一个Callable线程类的对象代表一个线程 Callable<String> myCallable = new MyCallable(100); // 4、把callable对象封装成一个futureTask对象 FutureTask<String> stringFutureTask = new FutureTask<>(myCallable); // 5、 把任务对象交给线程对象 // 启动线程 new Thread(stringFutureTask).start(); // 6、获取线程执行完成后的结果 String s = stringFutureTask.get(); System.out.println("s = " + s); } } 

什么是多线程

多线程常用方法

什么是多线程

package com.cqh.demo05; / * @author cqh * @date 2024/1/16 * @Description */ // 1、子类继承Thread线程类 public class MyThread extends Thread{ public MyThread(){ } // 构造器方法为参数设置名字 public MyThread(String name){ super(name); } // 重写Thread类的 run方法 @Override public void run() { // 描述线程执行任务 Thread thread = Thread.currentThread(); for (int i = 0; i <=5 ; i++) { if (i==5){ try { // 线程暂停5秒 再执行 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(thread.getName()+"子线程= " + i); } } } 

package com.cqh.demo05; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadTest01 { // main方法是由一条默认的主线程执行 public static void main(String[] args) throws InterruptedException { // 3、创建一个MyThread线程类的对象代表一个线程 Thread myThread = new MyThread(); //设置线程名字 myThread.setName("子1"); // 启动线程 myThread.start(); // 获取线程名字 System.out.println( myThread.getName()); MyThread thread2 = new MyThread("子2"); thread2.start(); // 该线程执行完后继续执行其他线程 thread2.join(); // 那个线程执行它它就会得到哪个线程对象 // 可以用这个方法获取主线程名称 Thread thread = Thread.currentThread(); thread.setName("不错的线程 "); for (int i = 0; i <=5 ; i++) { System.out.println( thread.getName()+"主线程 = " + i); } } } 

线程安全问题

多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。

取钱案例

什么是多线程

具体实现代码

用的是普通java项目

什么是多线程

package com.cqh.demo06; / * @author cqh * @date 2024/1/16 * @Description */ public class Account { private String carId;// 银行卡号 private double money;// 余额 public Account() { } public Account(String carId, double money) { this.carId = carId; this.money = money; } public String getCarId() { return carId; } public void setCarId(String carId) { this.carId = carId; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } @Override public String toString() { return "Account{" + "carId='" + carId + '\'' + ", money=" + money + '}'; } public void drawMoney(double money) { Thread thread = Thread.currentThread(); String name = thread.getName(); // 1、判断余额是否足够 if(this.money>=money){ System.out.println(name+"来取钱"+money+"成功"); this.money-=money; System.out.println(name+"取钱,余额剩余"+this.money); }else { System.out.println(name+"来取钱,余额不足"); } } } 

package com.cqh.demo06; / * @author cqh * @date 2024/1/16 * @Description */ public class DrawThread extends Thread{ private Account account; public DrawThread(Account account,String name) { super(name); this.account=account; } @Override public void run() { // 取钱 account.drawMoney(10000); } } 
package com.cqh.demo06; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadTest { public static void main(String[] args) { // 1、创建账户对象,代表两个人的共享账户 Account account = new Account("CardId-110",10000); // 2、创建两个线程分别代表小明、小红分别在同一个账户去钱 new DrawThread(account,"小明").start(); new DrawThread(account,"小红").start(); } } 

什么是多线程

线程同步 

解决线程安全问题

让多个线程实现先后依次访问共享资源,这样就解决了安全问题

加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来.

同步代码块

作用:把共享资源的核心代码给上锁,以保证线程安全

什么是多线程

原理: 每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行。

注意: 

什么是多线程

修改 

什么是多线程

效果 

什么是多线程

锁对象的使用规范
建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。
对于静态方法建议使用字节码 (类名.class) 对象作为锁对象

同步方法

作用:把访问共享资源的核心方法给上锁,以此保证线程安全

什么是多线程

原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行.

修改

什么是多线程

效果

什么是多线程

同步方法底层原理
同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码
如果方法是实例方法:同步方法默认用this作为的锁对象。
如果方法是静态方法:同步方法默认用类名.class作为的锁对象。 

同步代码块与同步方法比较

lock锁

Lock锁是]DK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大

Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。

什么是多线程

线程通信

什么是线程通信?

当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺
线程通信的常见模型(生产者与消费者模型)
生产者线程负责生产数据
消费者线程负责消费生产者生产的数据
注意:生产者生产完数据应该等待自己,通知消费者消费;消费者消费完数据也应该等待自己,再通知生产者生产

需求:

3个生产者线程,负责生产包子,每个线程每次只脑生产1个包子放在来子上,2个消费者线程负责吃包子,每人每次只能从集予上产1个包子吃。

什么是多线程

注意

上述方法应该使用当前同步锁对象进行调用

具体代码

package com.cqh.demo07; import java.util.ArrayList; import java.util.List; / * @author cqh * @date 2024/1/16 * @Description */ public class Desk { private List<String> list=new ArrayList<>(); // 生产 public synchronized void put() { try { String name = Thread.currentThread().getName(); // 判断有没有包子 if (list.size()==0){ list.add(name+"做的肉包子"); System.out.println(name+"做了一个肉包子"); Thread.sleep(2000); // 唤醒别人 等待自己 this.notifyAll(); this.wait(); }else { // 唤醒别人 等待自己 this.notifyAll(); this.wait(); } } catch (Exception e) { e.printStackTrace(); } } // 消费 public synchronized void get() { try { String name = Thread.currentThread().getName(); if (list.size()==1){ System.out.println(name+"吃了" + list.get(0)); list.clear(); Thread.sleep(1000); this.notifyAll(); this.wait(); }else { this.notifyAll(); this.wait(); } } catch (Exception e) { e.printStackTrace(); } } } 
package com.cqh.demo07; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadTest { public static void main(String[] args) { Desk desk = new Desk(); // 创建3个生产者 new Thread(()-> { while (true){ desk.put(); } },"厨师1").start(); new Thread(()-> { while (true){ desk.put(); } },"厨师2").start(); new Thread(()-> { while (true){ desk.put(); } },"厨师3").start(); //创建2个消费者 new Thread(()-> { while (true){ desk.get(); } },"消费者1").start(); new Thread(()-> { while (true){ desk.get(); } },"消费者2").start(); } } 

线程池 

什么是线程池?
线程池就是一个可以复用线程的技术。
不使用线程池的问题
 用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。

什么是多线程

JDK 5.0起提供了代表线程池的接口: ExecutorService。 

什么是多线程

如何得到线程池对象? 

方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象 

什么是多线程

方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象

什么是多线程

参数一: corePoolSize:指定线程池的核心线程的数量
参数二:maximumPoolSize: 指定线程池的最大线程数量。
参数三: keepAliveTime: 指定临时线程的存活时间
参数四: unit: 指定临时线程存活的时间单位(秒、分、时、天)
参数五:workQueue: 指定线程池的任务队列。

参数六: threadFactory: 指定线程池的线程工厂

参数七:handler;指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理) 

线程池的注意事项
1、临时线程什么时候创建?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
2、什么时候会开始拒绝新任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。

什么是多线程

处理runnable任务 

 什么是多线程

什么是多线程

什么是多线程

什么是多线程

什么是多线程

什么是多线程

什么是多线程 什么是多线程

package com.cqh.demo08; / * @author cqh * @date 2024/1/16 * @Description */ public class MyRunnable implements Runnable{ @Override public void run() { // 描述任务是做什么的 System.out.println(Thread.currentThread().getName()+"-----" ); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } 

package com.cqh.demo08; import java.util.concurrent.*; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadPoolTest { public static void main(String[] args) { // 1、通过ThreadPoolExecutor创建一个线程池对象 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor( 3, 5, 8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); MyRunnable myRunnable = new MyRunnable(); poolExecutor.execute(myRunnable);// 核心线程使用 poolExecutor.execute(myRunnable);// 核心线程使用 poolExecutor.execute(myRunnable);// 核心线程使用 poolExecutor.execute(myRunnable); poolExecutor.execute(myRunnable); poolExecutor.execute(myRunnable); poolExecutor.execute(myRunnable); // 到了临时线程的创建时机了 poolExecutor.execute(myRunnable); poolExecutor.execute(myRunnable); // 到了临时线程的创建时机 poolExecutor.execute(myRunnable); // poolExecutor.shutdown();// 等着线程池的任务全部执行完毕后,再关闭线程池 // poolExecutor.shutdownNow();// 立即关闭线程池 } } 

 处理Callable任务什么是多线程

package com.cqh.demo08; import java.util.concurrent.Callable; / * @author cqh * @date 2024/1/16 * @Description */ public class MyCallable implements Callable<String> { private int n; public MyCallable(int n){ } @Override public String call() throws Exception { int sum=0; for (int i = 0; i <=5 ; i++) { sum+=1; } return Thread.currentThread().getName()+"求出了1-"+n+"的和是"+sum; } } 

package com.cqh.demo08; import java.util.concurrent.*; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadPoolTest1 { public static void main(String[] args) throws Exception { // 1、通过ThreadPoolExecutor创建一个线程池对象 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor( 3, 5, 8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); // 使用线程处理callable任务 Future<String> submit1 = poolExecutor.submit(new MyCallable(100)); Future<String> submit2 = poolExecutor.submit(new MyCallable(200)); Future<String> submit3 = poolExecutor.submit(new MyCallable(300)); Future<String> submit4 = poolExecutor.submit(new MyCallable(400)); System.out.println(submit1.get()); System.out.println(submit2.get()); System.out.println(submit3.get()); System.out.println(submit4.get()); } } 

什么是多线程

Executors工具类实现线程池

概念:

它是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。

 什么是多线程

注意 : 这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象 

package com.cqh.demo08; import java.util.concurrent.*; / * @author cqh * @date 2024/1/16 * @Description */ public class ThreadPoolTest2 { public static void main(String[] args) throws Exception { // 1、通过Executors创建一个线程池对象 ExecutorService executorService = Executors.newFixedThreadPool(3); // 使用线程处理callable任务 Future<String> submit1 = executorService.submit(new MyCallable(100)); Future<String> submit2 = executorService.submit(new MyCallable(200)); Future<String> submit3 = executorService.submit(new MyCallable(300)); Future<String> submit4 = executorService.submit(new MyCallable(400)); System.out.println(submit1.get()); System.out.println(submit2.get()); System.out.println(submit3.get()); System.out.println(submit4.get()); } } 

计算密集型的任务:核心线程数=CPU的核数 + 1
I0集型的任务:核心程数量 = CPU核数 * 2

大型并发系统环境中使用Executors如果不注意可能会出现系统风险。

什么是多线程

并发和并行 

什么是进程?

正在运行的程序(软件)就是一个独立的进程

线程是属于进程的,一个进程中可以同时运行很多个线程

进程中的多个线程其实是并发和并行执行的

什么是并发?

进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发

简单来说就是:

在同一个时刻上,同时有多个线程在被CPU调度执行

线程的生命周期

Java线程的状态
Java总共定义了6种状态
6种状态都定义在Thread类的内部枚举类中

什么是多线程

 线程的6种状态互相转换

什么是多线程 什么是多线程

悲观锁和乐观锁

 悲观锁:一上来就加锁,没有安全感。每次只能一个线程进入访问完毕后,再解锁。 线程安全,性能较差!

乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题的时候才开始控制。线程安全,性能较好。

悲观锁案例

什么是多线程

什么是多线程

乐观锁

package com.cqh.demo09; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; / * @author cqh * @date 2024/1/16 * @Description */ public class MyRunnable2 implements Runnable { // 整数修改的乐观锁:原子类 private AtomicInteger count=new AtomicInteger(); @Override public void run() { for (int i = 0; i <=100 ; i++) { synchronized (this){ System.out.println(Thread.currentThread().getName()+"count = " + (count.incrementAndGet())); } } } } 

 练习题

目标: .有100份礼品,小红,小明两人同时发送,当剩下的礼品小于10份的时候则不再送出, 利用多线程模拟该过程并将线程的名称打印出来。并最后在控制台分别打印小红,小明各自送出多少分礼物。

package com.cqh.demo10; import java.util.List; import java.util.Random; / * @author cqh * @date 2024/1/17 * @Description */ public class SendThread extends Thread { private List<String> gift; private String name; private int count=0; public SendThread(List<String> gift, String name) { super(name); this.gift = gift; this.name = name; } @Override public void run() { Random random = new Random(); while (true) { if (gift.size() < 10) { return; } synchronized (gift) { String remove = gift.remove(random.nextInt(gift.size())); System.out.println(name+"发出了礼物" + remove); count++; } } } public int getCount() { return count; } public void setCount(int count) { this.count = count; } } 
package com.cqh.demo10; import jdk.nashorn.internal.ir.CallNode; import java.util.ArrayList; import java.util.Random; / * @author cqh * @date 2024/1/16 * @Description */ public class MyTest { public static void main(String[] args) throws InterruptedException { // 目标: .有100份礼品,小红,小明两人同时发送,当剩下的礼品小于10份的时候则不再送出, // 利用多线程模拟该过程并将线程的名称打印出来。并最后在控制台分别打印小红,小明各自送出多少分礼物。 ArrayList<String> gift = new ArrayList<>(); String[] names={"苹果手机","笔记本电脑","ipad","耳机","音响"}; Random random = new Random(); for (int i = 0; i <100 ; i++) { gift.add(names[random.nextInt(names.length)]+(i+1)); } SendThread sendThread1 = new SendThread(gift,"小明"); SendThread sendThread2 = new SendThread(gift, "小红"); sendThread1.start(); sendThread1.join(); sendThread2.start(); sendThread2.join(); System.out.println(sendThread1.getName() +"发送了"+ sendThread1.getCount()+"个礼物"); System.out.println(sendThread2.getName() +"发送了"+ sendThread2.getCount()+"个礼物"); } } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/143640.html

(0)
上一篇 2025-05-02 12:26
下一篇 2025-05-02 12:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信