`

多线程编程,不得不说的话题

阅读更多

多线程
线程:是指进程中的一个执行流程。
线程与进程的区别:每个进程都需要操作系统为其分配独立的内存地址空间,而同一进程中的所有线程在同一块地址空间中工作,这些线程可以共享同一块内存和系统资源。


如何创建一个线程?

创建线程有两种方式,如下:
1、 扩展java.lang.Thread类
2、 实现Runnable接口
Thread类代表线程类,它的两个最主要的方法是:
run()——包含线程运行时所执行的代码
Start()——用于启动线程

一个线程只能被启动一次。第二次启动时将会抛出java.lang.IllegalThreadExcetpion异常

线程间状态的转换(如图示)



新建状态:用new语句创建的线程对象处于新建状态,此时它和其它的java对象一样,仅仅在堆中被分配了内存
就绪状态:当一个线程创建了以后,其他的线程调用了它的start()方法,该线程就进入了就绪状态。处于这个状态的线程位于可运行池中,等待获得CPU的使用权
运行状态:处于这个状态的线程占用CPU,执行程序的代码
阻塞状态:当线程处于阻塞状态时,java虚拟机不会给线程分配CPU,直到线程重新进入就绪状态,它才有机会转到运行状态。
阻塞状态分为三种情况:
1、 位于对象等待池中的阻塞状态:当线程运行时,如果执行了某个对象的wait()方法,java虚拟机就回把线程放到这个对象的等待池中
2、 位于对象锁中的阻塞状态,当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他的线程占用,JVM就会把这个线程放到这个对象的琐池中。
3、 其它的阻塞状态:当前线程执行了sleep()方法,或者调用了其它线程的join()方法,或者发出了I/O请求时,就会进入这个状态中。

死亡状态:当线程退出了run()方法,就进入了死亡状态,该线程结束了生命周期。
           或者正常退出
           或者遇到异常退出
           Thread类的isAlive()方法判断一个线程是否活着,当线程处于死亡状态或者新建状态时,该方法返回false,在其余的状态下,该方法返回true.

线程调度
线程调度模型:分时调度模型和抢占式调度模型
JVM采用抢占式调度模型。
所谓的多线程的并发运行,其实是指宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务。
(线程的调度不是跨平台,它不仅取决于java虚拟机,它还依赖于操作系统)

如果希望明确地让一个线程给另外一个线程运行的机会,可以采取以下的办法之一
1、 调整各个线程的优先级
2、 让处于运行状态的线程调用Thread.sleep()方法
3、 让处于运行状态的线程调用Thread.yield()方法
4、 让处于运行状态的线程调用另一个线程的join()方法

调整各个线程的优先级
Thread类的setPriority(int)和getPriority()方法分别用来设置优先级和读取优先级。
如果希望程序能够移值到各个操作系统中,应该确保在设置线程的优先级时,只使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY这3个优先级。

线程睡眠:当线程在运行中执行了sleep()方法时,它就会放弃CPU,转到阻塞状态。
线程让步:当线程在运行中执行了Thread类的yield()静态方法时,如果此时具有相同优先级的其它线程处于就绪状态,那么yield()方法将把当前运行的线程放到运行池中并使另一个线程运行。如果没有相同优先级的可运行线程,则yield()方法什么也不做。
Sleep()方法和yield()方法都是Thread类的静态方法,都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程,两者的区别在于:
         1、sleep()方法会给其他线程运行的机会,而不考虑其他线程的优先级,因此会给较低线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。
2、当线程执行了sleep(long millis)方法后,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法后,将转到就绪状态。
          3、sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常
          4、sleep()方法比yield()方法具有更好的移植性

等待其它线程的结束:join()
          当前运行的线程可以调用另一个线程的 join()方法,当前运行的线程将转到阻塞状态,直到另一个线程运行结束,它才恢复运行。

定时器Timer:在JDK的java.util包中提供了一个实用类Timer, 它能够定时执行特定的任务。

线程的同步
原子操作:根据Java规范,对于基本类型的赋值或者返回值操作,是原子操作。但这里的基本数据类型不包括long和double, 因为JVM看到的基本存储单位是32位,而long 和double都要用64位来表示。所以无法在一个时钟周期内完成。

自增操作(++)不是原子操作,因为它涉及到一次读和一次写。

原子操作:由一组相关的操作完成,这些操作可能会操纵与其它的线程共享的资源,为了保证得到正确的运算结果,一个线程在执行原子操作其间,应该采取其他的措施使得其他的线程不能操纵共享资源。

同步代码块:为了保证每个线程能够正常执行原子操作,Java引入了同步机制,具体的做法是在代表原子操作的程序代码前加上synchronized标记,这样的代码被称为同步代码块。

同步锁:每个JAVA对象都有且只有一个同步锁,在任何时刻,最多只允许一个线程拥有这把锁。

当一个线程试图访问带有synchronized(this)标记的代码块时,必须获得 this关键字引用的对象的锁,在以下的两种情况下,本线程有着不同的命运。
1、 假如这个锁已经被其它的线程占用,JVM就会把这个线程放到本对象的锁池中。本线程进入阻塞状态。锁池中可能有很多的线程,等到其他的线程释放了锁,JVM就会从锁池中随机取出一个线程,使这个线程拥有锁,并且转到就绪状态。
2、 假如这个锁没有被其他线程占用,本线程会获得这把锁,开始执行同步代码块。
(一般情况下在执行同步代码块时不会释放同步锁,但也有特殊情况会释放对象锁
如在执行同步代码块时,遇到异常而导致线程终止,锁会被释放;在执行代码块时,执行了锁所属对象的wait()方法,这个线程会释放对象锁,进入对象的等待池中)

线程同步的特征:
1、 如果一个同步代码块和非同步代码块同时操作共享资源,仍然会造成对共享资源的竞争。因为当一个线程执行一个对象的同步代码块时,其他的线程仍然可以执行对象的非同步代码块。(所谓的线程之间保持同步,是指不同的线程在执行同一个对象的同步代码块时,因为要获得对象的同步锁而互相牵制)
2、 每个对象都有唯一的同步锁
3、 在静态方法前面可以使用synchronized修饰符。
4、 当一个线程开始执行同步代码块时,并不意味着必须以不间断的方式运行,进入同步代码块的线程可以执行Thread.sleep()或者执行Thread.yield()方法,此时它并不释放对象锁,只是把运行的机会让给其他的线程。
5、 Synchronized声明不会被继承,如果一个用synchronized修饰的方法被子类覆盖,那么子类中这个方法不在保持同步,除非用synchronized修饰。

线程安全的类:
1、 这个类的对象可以同时被多个线程安全的访问。
2、 每个线程都能正常的执行原子操作,得到正确的结果。
3、 在每个线程的原子操作都完成后,对象处于逻辑上合理的状态。

释放对象的锁:
1、 执行完同步代码块就会释放对象的锁
2、 在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放
3、 在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放对象锁,进入对象的等待池。

死锁
当一个线程等待由另一个线程持有的锁,而后者正在等待已被第一个线程持有的锁时,就会发生死锁。JVM不监测也不试图避免这种情况,因此保证不发生死锁就成了程序员的责任。

如何避免死锁
一个通用的经验法则是:当几个线程都要访问共享资源A、B、C 时,保证每个线程都按照同样的顺序去访问他们。

线程通信
Java.lang.Object类中提供了两个用于线程通信的方法
1、 wait():执行了该方法的线程释放对象的锁,JVM会把该线程放到对象的等待池中。该线程等待其它线程唤醒
2、 notify():执行该方法的线程唤醒在对象的等待池中等待的一个线程,JVM从对象的等待池中随机选择一个线程,把它转到对象的锁池中。

 

分享到:
评论

相关推荐

    Android编程开发指南

    内容涵盖了Android应用的生命周期、用户界面设计、数据存储、网络通信、多线程处理等关键话题,这些都是开发Android应用不可或缺的知识点。 此外,由于Android系统具有庞大的用户群体和广泛的应用场景,因此在开发...

    Introduction to 3D Game Programming with Directx 11_DX11龙书英文版

    本书名为《Introduction to 3D Game Programming with DirectX 11》,也被称为...书中不仅涵盖了DirectX 11编程的必要基础和高级概念,还包括了着色器编程和多线程渲染等核心话题,是游戏开发者不可或缺的学习资源。

    Visual Basic 2005 For Dummies (2009)

    对于有一定基础的开发者,本书还探讨了一些高级话题,如多线程编程、异常处理、性能优化、安全性等,帮助读者提升编程技巧,解决复杂问题。 ### 实战案例分析 为了加深理解和实践,书中包含多个实战案例,涵盖了...

    c99 学习笔记

    C语言的编程基础涉及数据类型、控制结构、函数、指针等核心概念,而高级编程则涵盖更复杂的话题,如动态内存管理、文件操作、多线程和进程间通信。 内容部分由于OCR扫描结果存在文字识别错误和遗漏,导致部分信息...

    12306火车票抢票程序(源码学习使用)

    【标题】"12306火车票抢票程序(源码学习使用)"涉及到的知识点主要集中在模拟抢票软件的开发技术上,这通常包括网络编程、多线程、定时任务、数据抓取和API接口调用等方面。12306是中国铁路客户服务中心的官方网站...

    500 JAVA Tips

    讲述了Java多线程编程的基础知识和高级技巧,帮助开发者编写高效并发的程序。 #### 15. Sound & Multimedia 介绍了如何使用Java处理音频和多媒体数据,包括声音播放、视频处理等功能。 #### 16. String, text, ...

    URscrip说明书

    例如,可以使用线程进行多任务处理,并通过线程调度机制来管理这些线程。 模块化编程是URscript编程中的一个重要概念。文档中介绍了多个模块,包括motion(动作)模块、internals(内部)模块、urmath(机器人数学...

    QQ号码申请器源码

    这就需要用到多线程或多进程技术,如Python的threading模块或Java的ExecutorService。 6. **异常处理**:在网络编程中,网络中断、超时等问题是常见的,因此良好的异常处理机制至关重要,以确保程序的稳定性和可靠...

    新版Android开发教程.rar

    ----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 开放手机联盟 --Open --Open --Open --Open Handset Handset Handset Handset Alliance ...

    java面试知识点.docx

    Java语言在面试中是不可或缺的重要部分,涉及到的知识点广泛且深入。...同时,面试者还需要了解其他Java特性,如异常处理、集合框架、多线程模型、垃圾回收机制以及设计模式等,这些都是Java面试的常见话题。

    Imperfect C++ Practical Solutions for Real-Life Programming

    即便是C++阵营里最忠实的信徒,也不得不承认:C++语言并不完美。实际上,世界上也没有完美的编程语言。. 如何克服C++类型系统的不足?在C++中,如何利用约束、契约和断言来实施软件设计?如何处理被C++标准所忽略的...

    java知识集合

    Java作为一门广泛使用的编程语言,...综上所述,Java的知识体系包括但不限于JVM运行机制、内存管理、垃圾回收、并发编程、IO/NIO、类加载机制等多个方面,Java开发者在学习过程中需要对这些知识点有系统的理解和掌握。

    难得的英文java技巧汇总

    - **并发编程**:多线程编程的基础和最佳实践。 - **性能优化**:提高Java应用程序性能的技巧。 - **设计模式**:常用的软件设计模式及其应用场景。 - **面试准备**:常见的Java面试问题及其解答。 综上所述,...

    iOS Apprentice Sixth Edition

    - 多线程编程的最佳实践。 - Core Animation框架的深度探索。 - 使用Core ML进行机器学习应用开发。 - ARKit与RealityKit的AR应用开发技巧。 #### 五、书籍价值与目标读者 - **书籍价值**:本书不仅提供了全面...

    基于Python的网络爬虫技术.zip

    1. 多线程与协程:利用Python的threading或gevent库提高爬虫效率。 2. 数据可视化:使用matplotlib、seaborn等库对爬取的数据进行分析和可视化。 3. 云服务与分布式爬虫:利用AWS、Docker等云服务,结合Scrapy-Redis...

    Apache Spark 2.x for Java Developers

    在Java开发者学习使用Spark的过程中,了解Java的面向对象编程原则、多线程处理、集合框架等基础知识是基础。同时,为了更有效地利用Spark进行大数据处理,还需要熟悉Hadoop生态系统的其它组件,比如HDFS、HBase等。 ...

    网站页面下载

    10. **性能优化**:大规模页面下载要考虑效率问题,如多线程、分布式爬虫等技术可以提高下载速度。同时,合理设置下载间隔和重试机制,防止对目标服务器造成过大压力。 以上就是关于“网站页面下载”的一些关键知识...

    C#微软培训资料

    14.2 多 态 性 .159 14.3 抽象与密封 .163 14.4 继承中关于属性的一些问题.169 14.5 小 结 .172 第四部分 深入了解 C#.174 第十五章 接 口 .174 15.1 组件编程技术 .174 15.2 接 口 定 义 .177 15.3 ...

Global site tag (gtag.js) - Google Analytics