多线程
线程:是指进程中的一个执行流程。
线程与进程的区别:每个进程都需要操作系统为其分配独立的内存地址空间,而同一进程中的所有线程在同一块地址空间中工作,这些线程可以共享同一块内存和系统资源。
如何创建一个线程?
创建线程有两种方式,如下:
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从对象的等待池中随机选择一个线程,把它转到对象的锁池中。
- 大小: 701.5 KB
分享到:
相关推荐
总结来说,Pker多线程后台急速扫描器是网络安全领域的一款实用工具,它通过多线程技术提高扫描效率,专门针对后台系统进行安全检测。正确且合规地使用Pker,能够帮助用户及时发现并解决安全隐患,提升系统的整体安全...
BMDThread最新多线程控件,支持D5 D6 D7 D2005 D2006 D2007 D2009 D2010,包含全部程序文件,主控件程序含源代码,在D7上安装和运行正常。 我在用Delphi5时就很喜欢这个控件,后来到D7以后这个控件就不能用了,不得...
为了避免多个线程同时读写一个数据而产生不可预料的后果,我们需要将各个线程 对同一个数据的访问做同步。所谓同步,是指在一个线程访问数据未结束...其他线程不得对同一个数据进行访问。如此,对数据的访问被原子化了
进行多线程编程就不得不考虑访问共享资源的问题。Java同步机制可以控制线程代码重入,但很多时候多个线程在各自的执行过程中,会访问诸如存储处理等同一处资源,可能会导致线程阻塞和死锁的情况。
MIPS程序员指南系列是MIPS架构的官方程序员参考资料,其中卷IV-f专注于介绍MIPS32架构下的多线程模块(MIPS MT Module)及其指令集。 多线程技术是现代处理器设计中的一个重要方面,它允许同时执行多个线程来提高...
1. 多线程处理:该助手采用多线程技术,可以同时处理多个店铺的订单,无需人工逐一操作,大大减少了商家的工作负担,提高了出餐速度,确保了订单的及时性。 2. 无限店铺登录:无论商家拥有多少家分店,都可以通过这...
注意:IO 操作无法使用多线程,IO 操作的并发率为 0,也就是说不支持并发。使用多个线程读写文件,比单个线程会更慢,因为带来了更多的寻道时间。 优化思路: 1. 读取文件基于流,每次读一行,自行判断换行符号,...
在探讨多线程编程时,不得不提的是操作系统提供的API。例如,MFC(Microsoft Foundation Classes)是微软公司提供的一个C++库,用于简化Windows应用程序的开发。在多线程方面,MFC提供了创建进程和线程的功能,例如...
讲到多线程技术时,就不得不提及"进程"这个概念了。百度对进程的接受如下: 进程:是操作系统的基础,是一次程序的执行,是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合运行的过程,他...
这就不得不说个人了,他就是solaris,这方面的佼佼者。传统的Unix也支持线程的概念,但是在一个进程(process,是操作系统结构的基础;是一个正在执行的程序;计算机中正在运行的程序实例;可以分配给处理器并由...
不得不看的excelvba多程序实例,附注释,word格式
STA 一个对象只能由一个线程访问(通过对象的接口指针调用其方法),其他线程不得访问这个对象,因此对于这个对象的所有调用都是同步了的,对象的状态(也就是对象的成员变量的值)肯定是正确变化的,不会出现线程...
但牵扯到几个给用户提供更好的“用户体验”的需求,就不得不使用多线程了,需求如下:(1)在图二对应的功能界面上,用户在发表完成之后,应该可以立即关闭;(2)在各个平台的独立发表界面,在发表完成之后,发表界面...
这就不得不说个人了,他就是solaris,这方面的佼佼者。传统的Unix也支持线程的概念,但是在一个进程(process,是操作系统结构的基础;是一个正在执行的程序;计算机中正在运行的程序实例;可以分配给处理器并由...
总结来说,这个项目是一个基于Django的后台应用,利用多线程和定时任务来高效地从各个网站抓取热点头条,实现了数据的实时更新,并且可能具备灵活的扩展性和可定制性。对于想要学习Web开发和网络爬虫的开发者来说,...
我就是当时没有打好基础,最后学习Java web、Java框架的时候对这些东西很难有深刻的理解,所以我不得不回头来恶补基础。今天我们就来谈谈并发那些事。 并发 并发就是将程序划分为多个任务,每个任务可以在同一时间内...
和其它多线程一样,不能在IDE环境调试,只能编译EXE后再调试。 特别声明:此DLL仅限于VB爱者研究使用,使用者请注明出处(首发于VBGOOD 链接地址http://www.vbgood.com/thread-108165-1-1.html),不得用于商业用途...
《Android那些不得不说的事儿》这本书深入浅出地探讨了Android平台开发的各种关键知识点和技术细节,对于想要深入了解Android开发的读者来说是一本不可多得的参考资料。以下是对书中的部分核心内容进行的详细解读: ...
了解多线程,我们不得不提到与之相关的多进程以及操作系统的基础知识。 在单CPU环境下,尽管硬件只有一个处理器,但操作系统通过任务调度策略使得看似多个任务同时运行。常见的调度算法有: 1) 优先级调度:根据...
初学编程基本上都是单线程结构化编程,但随着编程能力的提高,以及应用场景的复杂多变,我们不得不要面临多线程并发编程。 多线程并发编程中,需要注意线程安全问题。如果你的线程不足够“安全”,程序就可能出现...