本篇文章为你整理了Tread多线程(多线程tls)的详细内容,包含有多线程 thread 多线程tls thread多线程写法 twisted 多线程 Tread多线程,希望能帮助你了解 Tread多线程。
多线程是什么?
多线程是指从软硬件上实现的多条执行流程的技术(多条线程由cpu负责调度执行)。
多线程的创建方式
方式一:继承Thread
①定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
②创建MyThread类的对象
③调用线程对象的start()方法启动线程(启动后还是执行run方法的)
//(1)让自定义的MyThread继承Thread线程类【自定义的类也就具备线程的特性】
public class MyThread extends Thread {
//(2)想要声明自定义的线程执行的时候到底执行什么代码,主动重写父类的run方法
@Override
public void run() {
for (int i = 1; i = 20; i++) {
System.out.println("【自定义线程】的run方法执行了第" + i + "次!");
public class ThreadTest1 {
public static void main(String[] args) {
//(3)创建自定义线程类对象并调用start方法启动线程
MyThread myThread = new MyThread();
myThread.start();
//补:在自定义线程启动之后,继续编写代码让主线程执行
for (int i = 1; i = 20; i++) {
System.out.println("【主线程】的run方法执行了第" + i + "次!");
}
方式一优缺点
方式二:实现Runnable接口
①定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
②创建MyRunnable任务对象
③把MyRunnable任务对象交给Thread处理。
public class ThreadTest2 {
public static void main(String[] args) {
//(3)创建MyRunnable线程任务对象 【和线程还没有关系】
MyRunnable myRunnable = new MyRunnable();
//(4)创建Thread线程对象,并且线程任务作为构造方法的参数传递【枪:√ 弹夹:√】
Thread t = new Thread(myRunnable);
t.start();
//补:在自定义线程启动之后,继续编写代码让主线程执行
for (int i = 1; i = 20; i++) {
System.out.println("【主线程】的run方法执行了第" + i + "次!");
}
方式二优缺点:
前两种线程创建文件都存在的一个问题
假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果。
解决(多线程的第三种创建方式)
利用Callable,FutureTask类型实现。
①创建任务对象
定义一个类实现Callable接口,重写call方法,封装要做的事情和要返回的数据。
把Callable类型的对象封装成FutureTask对象(线程任务对象)。
②把线程任务对象封装成Thread对象。
③调用Thread对象的start方法启动线程。
④线程执行完毕后,通过FutureTask对象的的get方法去获取线程任务执行的结果。
public class ThreadTest4 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//(3)Thread类不支持直接传递一个Callable线程任务对象【封装FutureTask对象并且将Callable线程任务作为构造参数传递】
MyCallable myCallable = new MyCallable();
FutureTask Integer futureTask = new FutureTask (myCallable);
//(4)创建Thread类对象并且将FutureTask作为参数传递
Thread t = new Thread(futureTask);
t.start();
//★(5)通过futureTask对象获取结果
Integer result = futureTask.get();
System.out.println("带有返回值的线程任务执行完成后返回的结果是:" + result);
//补:在自定义线程启动之后,继续编写代码让主线程执行
for (int i = 1; i = 20; i++) {
System.out.println("【主线程】的run方法执行了第" + i + "次!");
}
方式三优缺点:
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果。
三种线程创建方法比较
Thread的常见用法
public void run() 线程的任务方法
public void start() 启动线程
public Strign getName() 获取1当前线程名称,线程名称默认是Thread-索引
public void setName(String name) 为线程设置名称
public static Thread currentThread() 获取当前执行的线程对象
public static void sleep(long time) 让当前执行的线程休眠多少毫秒后,再继续执行
public void join() 让调用这个方法的线程先执行完
public class MyRunRunable implements Runnable {
@Override
public void run() {
for (int i = 1; i = 200; i++) {
//在打印的时候,想要【获取到执行当前这行代码的线程】的线程名称
//通过★Thread.currentThread():获取当前执行此方法的线程对象
System.out.println(Thread.currentThread().getName() + "已经跑了" + i + "米!");
public class ThreadTest5 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
String threadName = Thread.currentThread().getName();
System.out.println("【主线程名称】:" + threadName);
MyRunRunable myRunRunable = new MyRunRunable();
//线程起名方式(1):通过线程对象调用setName方法传递名称
Thread t1 = new Thread(myRunRunable);
t1.setName("张二狗");
//思考:模拟两个人跑 = 两个线程跑【跑的逻辑一样 所以使用同一个线程任务】不会干扰【底层:线程栈 线程执行过程中产生的变量数据都在线程栈中保存】
//线程起名方式(2):通过new Thread构造方法的时候,将参数一作为线程任务,参数二作为线程名称
Thread t2 = new Thread(myRunRunable, "刘铁柱");
t1.start();
//t1.join(); 【让调用此方法的线程先执行完:插队】
t2.start();
}
什么是线程安全问题?
多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。
取钱的线程安全问题
场景:小明和小红是一对夫妻,他们有一个共同的账户,余额是10万元,如果小明和小红同时来取钱,并且2人各自都在取钱10万元,可能会出现什么问题呢?
线程安全问题出现的原因?
String name = Thread.currentThread().getName();
System.out.println(LocalTime.now() + " " + name + "准备开始取钱!");
if (this.money = money) {
System.out.println(LocalTime.now() + " " + name + "取出了" + money + "元!");
this.money -= money;
} else {
System.out.println(LocalTime.now() + " " + name + "余额不足!");
System.out.println(LocalTime.now() + " 账户的余额是:" + this.money + "元!");
public String getAccoundId() {
return accoundId;
public void setAccoundId(String accoundId) {
this.accoundId = accoundId;
public Integer getMoney() {
return money;
public void setMoney(Integer money) {
this.money = money;
public Account() {
public Account(String accoundId, Integer money) {
this.accoundId = accoundId;
this.money = money;
}
定义线程类
public class TakeMoneyRunnable implements Runnable {
//线程任务需要访问到Account账户对象【将账户对象作为线程任务的构造方法 并且只给出一个有参构造】
private Account account;
public TakeMoneyRunnable(Account account) {
this.account = account;
@Override
public void run() {
account.takeMoney(100000);
}
测试类
package com.itheima.safe;
public class TakeMoneyThreadTest {
public static void main(String[] args) {
Account account = new Account("CHINA-BANK-62261728738", 100000);
//创建线程任务【由于两个线程的逻辑一样 只需要一个线程任务】
TakeMoneyRunnable takeMoneyRunnable = new TakeMoneyRunnable(account);
//创建线程对象并且传递线程任务和线程名称
Thread t1 = new Thread(takeMoneyRunnable, "张二狗");
Thread t2 = new Thread(takeMoneyRunnable, "王美丽");
t1.start();
t2.start();
线程同步(解决线程安全)
线程同步的思想
让多个线程实现先后依次访问共享资源,这样就解决了安全问题。
同步代码块
作用:把访问共享资源的核心代码给上锁,以此保证线程安全。
原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程次才可以来执行。
对线程安全改造
public void takeMoney(Integer money) {
String name = Thread.currentThread().getName();
System.out.println(LocalDateTime.now() + "" + name + "准备开始取钱");
//(同步代码块)加锁
synchronized (this) {
if (this.money = money) {
System.out.println(LocalDateTime.now() + "" + name + "取出了" + money + "元");
this.money -= money;
} else {
System.out.println(LocalDateTime.now() + "" + name + "余额不足");
System.out.println(LocalDateTime.now() + "账户的余额是" + this.money + "元");
}
同步锁的注意事项
作用:把访问共享资源的核心代码给上锁,以此保证线程安全。
原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程次才可以来执行。
对线程安全改造
//同步方法加锁(修饰符后面,放回值类型前面)
public synchronized void takeMoney(Integer money) {
String name = Thread.currentThread().getName();
System.out.println(LocalDateTime.now() + "" + name + "准备开始取钱");
if (this.money = money) {
System.out.println(LocalDateTime.now() + "" + name + "取出了" + money + "元");
this.money -= money;
} else {
System.out.println(LocalDateTime.now() + "" + name + "余额不足");
System.out.println(LocalDateTime.now() + "账户的余额是" + this.money + "元");
}
同步方法底层原理
Lock锁
Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。
Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。
对线程安全改造
private static final Lock LOCK = new ReentrantLock();
public void takeMoney(Integer money) {
LOCK.lock();
try {
String name = Thread.currentThread().getName();
System.out.println(LocalDateTime.now() + "" + name + "准备开始取钱");
if (this.money = money) {
System.out.println(LocalDateTime.now() + "" + name + "取出了" + money + "元");
this.money -= money;
} else {
System.out.println(LocalDateTime.now() + "" + name + "余额不足");
System.out.println(LocalDateTime.now() + "账户的余额是" + this.money + "元");
}finally {
LOCK.unlock();
了解线程池
什么是线程池?
线程池就是一个可以复用线程的技术。
不使用线程池的问题
用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理,而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程,这样会严重影响系统的性能。
线程池的工作原理
创建线程池
如何得到线程池对象?
方式一:使用ExecutorService的实现类ThreadPoolExecutor创建一个线程池对象。
方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。
ThreadPoolExecutor**构造器**
参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)
public static void main(String[] args) {
//基于ThreadPoolExecutor的构造方法创建线程池对象
//核心线程:【线程任务:Cpu密集型(运算):当前机器Cpu的核心数+1 Runtime.getRuntime().availableProcessors()+1】
//核心线程:【线程任务:IO密集型(读写):当前机器Cpu的核心数*2 Runtime.getRuntime().availableProcessors()*2】
//ThreadFactory:线程工厂 【Exectors.defaultThreadFactory】 获取默认的线程工厂
ThreadPoolExecutor pool = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() + 1, 15, 40L, TimeUnit.SECONDS,
new ArrayBlockingQueue (3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
//可以基于线程池规范接口的execute方法提交线程任务交给线程池执行
for (int i = 1; i = 25; i++) {
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了线程任务!");
//AbortPolicy:默认丢弃新任务并且抛出异常
//DiscardPolicy:默认丢弃新任务并且不抛出异常
//DiscardOldestPolicy:默认将等待时间最长的任务丢弃,并且让新任务添加到队列中
//CallerRunsPolicy:使用主线程执行新任务绕过当前线程池
//线程池一旦提交任务就持久运行【想要关闭调用shutdown/shutdownNow】
pool.shutdown();
}
线程池的注意事项
1、临时线程什么时候创建?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
2、什么时候会开始拒绝新任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。
线程池如何处理Runnable任务?
并发的含义
进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。
并行的理解
在同一个时刻上,同时有多个线程在被CPU调度执行。
简单说说多线程是怎么执行的?
以上就是Tread多线程(多线程tls)的详细内容,想要了解更多 Tread多线程的内容,请持续关注盛行IT软件开发工作室。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。