Tread多线程(多线程tls)

  本篇文章为你整理了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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: