`
jaybril
  • 浏览: 50283 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

Java必知必会之(四)---多线程全揭秘(上)

 
阅读更多

Java必知必会之(四)---多线程全揭秘(上)

本文旨在用最通俗的语言讲述最枯燥的基本知识。

全文提纲:
1.线程是什么?(上)
2.线程和进程的区别和联系(上)
3.创建多线程的方法(上)
4.线程的生命周期(上)
5.线程的控制(上)
6.线程同步(下)
7.线程池(下)
8.ThreadLocal的基本用法(下)
9.线程安全(下)


1.线程是什么

线程是进程中的一个执行流程,是被系统独立调度和分派的基本单位。

线程是什么?进程是什么?
这么说可能有点懵逼,举个栗子吧:

A工厂是一个生产汽车的工厂,今天员工张三去送货,员工李四去进货。这里的

  1. A工厂是一个进程(当然荒废的没有生命迹象的工厂不能算进程了)
  2. 员工张三去送货 是一个线程
  3. 员工李四去进货 也是一个线程

从例子可以看出
进程是指运行中的程序(没运行的程序,系统是不会为之分配资源的),每个进程都有自己独立的内存空间,当一个程序进入内存运行时,程序内部可能包含多个程序执行流,这个程序执行流就是一个线程。

几乎所有操作系统都支持多线程并发,就像我们平时上班用的电脑,我们可能习惯打开eclipse写代码,同时打开网易云音乐听课,而且还要打开有道翻译时刻准备着把我们的中文转成英文…
可见我们的电脑可以支持多个应用程序同时执行的,但实际上,而对于每个CPU来说,它在一个时间点内,只能执行一个程序,也就是一个进程,那为什么我们同时打开这么多程序运行没问题呢?
那是因为现代电脑都不止一个CPU啦。当然这个是一个原因。
最主要的是因为在程序运行过程中,CPU在不同程序之间高速的来回切换执行,因此所谓的“并发执行”实际上并不是多个程序在同时执行,而是系统对程序的执行做了调度,让视觉上看起来是同时执行了。
所以线程中的并发:
是指多个进程被CPU快速的轮换执行,而不是同时执行

2. 线程和进程的区别

通过上面的原理讲述已经能看出区别了,最主要有三点

  1. 线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
  2. 线程是进程中的一个执行流程,一个进程可以包含多个线程
  3. 3.

3. 多线程的创建

1. 继承Thread类创建线程:

 1public class ThreadTest  extends Thread {
 2    @Override
 3    public void run() {
 4        // 业务逻辑
 5        super.run();
 6    }
 7
 8    public static void main(String[] args) {
 9        new ThreadTest().run();
10        new ThreadTest().run();
11    }
12}

2. 实现Runnable接口

 1public class ThreadTest  implements Runnable {
 2    @Override
 3    public void run() {
 4         //业务逻辑
 5    }
 6
 7    public static void main(String[] args) {
 8        new ThreadTest().run();
 9        new ThreadTest().run();
10    }
11}

3. 使用Callable和Future创建

Callable接口是jdk5之后的新接口,它提供了一个call方法作为线程执行体,和thread的run方法类似,但是它的功能更强大:

  • 它可以有返回值
  • 它可以声明抛出异常
    因此也可以像Runnable一样,创建一个Callable对象作为Thread的target,而实现它的call方法作为执行体.
    同时jdk5提供了Future接口来代表Callable接口里的call方法返回值,并为Future接口提供了一个FutureTask实现类,该实现类实现了Future接口,并实现了Runable接口,它有以下几个方法:
  1. boolean cancal(boolean mayInterruptRunning):试图取消该Future里关联的Callable任务
  2. V get():返回Callable任务里call()方法的返回值,调用该方法将导致程序阻塞,必须等到子线程结束后才会得到返回值
  3. V get(long timeout,TimeUnit unit):
  4. boolean isCancel():如果在Callable任务正常完成前被取消,则返回true
  5. boolean isDone():如果Callable任务已完成,则返回true
 1public static void main(String[] args) {
 2
 3        //1)创建一个Callable实现类,并实现call方法
 4        //2)用FutrueTask来包装类的实例
 5     FutureTask ft=new FutureTask<>(new Callable<Integer>() {
 6        @Override
 7        public Integer call() throws Exception {
 8            System.out.println("执行了");
 9            try {
10                Thread.sleep(1000*5);
11            } catch (InterruptedException e1) {
12                // TODO Auto-generated catch block
13                e1.printStackTrace();
14            }
15
16            return 12;
17        }
18    });
19     //使用FutureTask对象作为target来创建并且启动线程
20     new Thread(ft).start();
21
22     //阻塞方式获取线程返回值
23     try {
24        System.out.println("返回值:"+ft.get());
25    } catch (InterruptedException e) {
26        // TODO Auto-generated catch block
27        e.printStackTrace();
28    } catch (ExecutionException e) {
29        // TODO Auto-generated catch block
30        e.printStackTrace();
31    }
32     //带有超时方式获取线程返回值
33     try {
34        System.out.println("返回值:"+ft.get(2,TimeUnit.SECONDS));
35    } catch (InterruptedException | ExecutionException | TimeoutException e) {
36        // TODO Auto-generated catch block
37        e.printStackTrace();
38    }
39
40    }

4. 线程的生命周期

线程创建之后,不会立即处于运行状态,根据前面对并发的定义理解:即使他启动了也不会永远都处于运行状态,如果它一直处于运行状态,就会一直占据着CPU资源,线程之间的切换也就无从谈起了。因此,线程是有生命周期的,他的生命周期包括以下几种状态:

  1. 新建(NEW)
  2. 就绪(Runnable)
  3. 运行(Running)
  4. 阻塞(Blocked)
  5. 死亡(Dead)
1. 新建状态

当在程序中用new创建一个线程之后,它就处于新建状态,此时它和程序中其它对象一样处于初始化状态(分配内存、初始化成员变量)。

2.就绪状态

当程序调用了start方法之后,程序就处于就绪状态,jvm会为它创建方法调用栈和程序计数器,此时的线程状态为可运行状态,并没有运行,而是需要线程调度器的调度决定何时运行。

3. 运行状态

当就绪的线程获得CPU之后,就会执行线程执行体(run方法),这时候线程就处于了运行状态。

4.阻塞状态

处于运行的状态的线程,除非执行时间非常非常非常短,否则它会因为系统对资源的调度而被中断进入阻塞状态。操作系统大多采用的是抢占式调度策略,在线程获得CPU之后,系统给线程一段时间来处理任务,当到时间之后,系统会强制性剥夺线程所占资源,而分配别的线程,至于分配给谁,这个取决于线程的优先级。

5.死亡状态

处于运行状态的线程,当它主动或者被动结束,线程就处于死亡状态。至于结束的形式,通常有以下几种:

  1. 线程执行完成,线程正常结束
  2. 线程执行过程中出现异常或者错误,被动结束
  3. 线程主动调用stop方法结束线程

5.线程的控制

Java提供了线程在其生命周期中的一些方法,便于开发者对线程有更好的控制。
主要有以下方法:

  1. 等 待:join()
  2. 后 台:setDeamon()
  3. 睡 眠:sleep()
  4. 让 步:yield()
  5. 优先级:setPriority()

1.线程等待

当某个线程执行流中调用其他线程的join()方法时,调用线程将被阻塞,知道被join()方法加入的join()线程执行完成为止。

乍一看,怎么也理解不了,这句话,我们来写一个程序测试一下:

 1public class ThreadTest  extends Thread {
 2    @Override
 3    public void run() {
 4        System.out.println(getName()+"运行...");
 5        for(int i=0;i<5;i++){
 6            System.out.println(getName()+"执行:"+i);
 7        }
 8    }
 9    public ThreadTest(String name){
10        super(name);
11    }
12    public static void main(String[] args) {
13        //main方法--主线程
14        //线程1
15        new ThreadTest("子线程1").start();
16        //线程2
17        ThreadTest t2=new ThreadTest("子线程2");
18        t2.start();
19
20        try {
21          t2.join(1000);
22        } catch (InterruptedException e) {
23          e.printStackTrace();
24        }
25        //线程3
26        new ThreadTest("子线程3").start();
27    }
28}

看输出结果:

 1子线程1运行...
 2子线程2运行...
 3子线程2执行:0
 4子线程2执行:1
 5子线程2执行:2
 6子线程2执行:3
 7子线程1执行:0
 8子线程2执行:4
 9子线程1执行:1
10子线程1执行:2
11子线程1执行:3
12子线程1执行:4
13子线程3运行...
14子线程3执行:0
15子线程3执行:1
16子线程3执行:2
17子线程3执行:3
18子线程3执行:4

可以看到,线程1和2在并发执行着,而线程3则在他们都执行完之后才开始。
由此可知:
join()方法调用之后,后面的线程必须等待前面执行完之后才能执行,而不是并发执行

2.线程转入后台

当线程调用了setDaemon(true)之后,它就转入为后台线程,为前台线程提供服务,而当前台所有线程死亡时,后台线程也会接受到JVM的通知而自动死亡。

1ThreadTest t2=new ThreadTest("子线程2");
2//这是为后台线程,但必须在start前设置,因为前台线程死亡JVM会通知
3//后台线程死亡,但接受指令到响应需要时间。因此要自爱start前就设置
4        t2.setDaemon(true);
5        t2.start();

3. 线程睡眠

当需要某个处于运行状态的线程暂停执行并且进入阻塞状态时,调用Thread.sleep既可。

4.线程让步

当需要某个处于运行状态的线程暂停执行并且进入就绪状态,调用
Thread.yield()即可

5.线程优先级

前面说到,系统分配CPU给哪个线程的执行,取决于线程的优先级,因此每个线程都有一定的优先级,优先级高的线程会获得更多的执行机会,默认情况下,每个线程的默认优先级都与创建它的父线程优先级一致。
当我们需要某个线程或者更多的执行机会时,调用
Thread.currentThread().setPriority(int newPriority);
方法即可,newPriority的范围在1~10。

 

觉得本文对你有帮助?请分享给更多人

关注「编程无界」,提升装逼技能

 

 

0
0
分享到:
评论

相关推荐

    揭秘Java虚拟机

    同时,Java天生支持多线程,JVM在内部维护线程上下文,确保并发执行的正确性。 8. **安全机制**:JVM提供了安全管理器,限制了Java代码对操作系统资源的访问,以确保程序的安全运行。 9. **兼容性和移植性**:由于...

    Android应用开发揭秘----书籍所需源码

    《Android应用开发揭秘》是一本深入探讨Android应用开发技术的专著,旨在帮助开发者深入了解Android系统的工作机制,并提升他们的应用开发技能。这本书的源码提供了丰富的实例,涵盖了Android开发的各个方面,包括...

    Android应用开发揭秘pdf

    多线程是Android开发中的复杂话题,书中会介绍Handler、Looper和Message机制,以及如何使用AsyncTask进行异步操作。理解这些内容有助于优化应用性能,避免UI线程阻塞。 最后,本书可能还会讲解一些高级主题,如JNI...

    Android应用开发揭秘.rar

    除此之外,Android应用的权限管理、多线程处理、异步任务(AsyncTask)、网络编程(HttpClient、OkHttp、Retrofit等)以及数据库SQLite的使用也是书中会详细阐述的内容。 在性能优化方面,书中可能会探讨内存管理、...

    android应用开发揭秘.pdf

    关于多线程和异步处理,书中会介绍Handler、Looper、IntentService以及现代的AsyncTask和LiveData,让开发者了解如何在Android环境中高效地进行后台操作。 移动设备上的资源管理也是重要一环,包括内存管理、电量...

    Android应用开发揭秘

    Android应用开发还包括对多线程和并发的处理,以及性能优化的策略。书中会讲解如何使用Handler、Looper和ThreadPool,以及如何通过内存分析和代码优化来提高应用的运行效率。 此外,书中还可能涉及Android的最新...

    《Android应用开发揭秘》PDF

    Android应用运行在自己的进程中,多线程是提高应用性能的关键。书中会讨论如何在Android中创建和管理线程,以及使用Handler、AsyncTask等机制来处理异步任务。 十、数据存储 Android提供了多种数据存储方式,如...

    Android应用开发揭秘源码

    同时,线程管理和异步处理(如AsyncTask、Handler、IntentService)也是Android开发中必不可少的部分,源码分析可以让我们看到如何在多线程环境中保证应用的稳定运行。 对于更高级的话题,例如权限管理、组件间通信...

    android应用开发揭秘1+2+3

    随着学习的深入,《Android应用开发揭秘_02.pdf》可能会探讨更复杂的话题,如多线程处理、服务(Service)、广播接收器(BroadcastReceiver)和内容提供者(ContentProvider)。这些组件是Android应用程序实现后台运行、跨...

    世界500强面试题精彩选载

    Java的基础语法、面向对象特性、集合框架、多线程、IO流、异常处理、JVM内存模型等都是面试的重点。熟悉Java标准库和常见设计模式也是必不可少的。 3. **C++**:C++面试通常会考察对象导向编程、模板、STL(标准...

    《Android应用开发揭秘》源码

    第12章的源码是书中的一个重要部分,它可能涵盖了高级UI设计、数据存储、网络通信、多线程、性能优化等多个关键知识点。以下是基于这个章节源码可能涉及的一些Android开发核心内容的详细说明: 1. **高级UI设计**:...

    深入理解Android:卷I--详细书签版

    5.2 以“三板斧”揭秘RefBase、 sp和WP 96 5.2.1 第一板斧——初识影子对象 96 5.2.2 第二板斧——由弱生强 103 5.2.3 第三板斧——破解生死魔咒 106 5.2.4 轻量级的引用计数控制类LightRefBase 108 5.2.5 题...

    Android设计思想解读开源框架.pdf

    - **组件**:Dagger的核心概念之一,用于声明依赖注入的模块和绑定规则。 ### 第八章:Android架构组件Jetpack **8.1 LiveData原理** - **LiveData**:是一个观察者模式的实现,用于在ViewModel和Activity之间...

    水果忍者点击屏幕效果.zip

    首先,我们要明白,Android应用程序主要由Java编写,因此,了解Java语言基础是必不可少的。在"水果忍者点击屏幕效果"中,我们关注的是用户的触摸事件处理。在Android中,这一过程通常涉及到`View`类的`onTouchEvent...

Global site tag (gtag.js) - Google Analytics