- 浏览: 264175 次
- 性别:
- 来自: 深圳
文章分类
- 全部博客 (88)
- JAVA / base (26)
- JAVA / web (12)
- JAVA / Lib-tools (5)
- SERVER / tomcat (4)
- DB / mysql (4)
- DB / mongodb (2)
- DB / memcached (2)
- DB / redis (2)
- WEB / Front-end (3)
- WEB / security (4)
- WEB / css (2)
- WEB / js (4)
- OS / linux (3)
- IT / Architecture (4)
- IT / other (2)
- Android (9)
- Go (1)
- Other (1)
- OS / Mac (2)
最新评论
-
Zero2Max:
哈哈,马士兵老师也发现了。
java实现接口的bug -
xly1981:
能像CSRF攻击一样带个图就更棒了
XSS跨站攻击 -
xmong:
df274119386 写道在javascript中看到下面的 ...
CSRF攻击与防御策略 -
df274119386:
在javascript中看到下面的语句 e.value = t ...
CSRF攻击与防御策略 -
xmong:
yzxqml 写道xmong 写道yzxqml 写道tomca ...
Tomcat集群
Java多线程
目录
1 线程的概念 1
1.1 线程的概念 1
1.2 进程与线程 1
2 线程的创建 2
2.1 JAVA创建线程 2
2.1.1 实现Runnable接口 2
2.1.2 扩展Thread类 4
2.2 JAVA线程的实例化,启动,执行 6
2.2.1 Java线程的实例化 6
2.2.1 Java线程的启动 7
2.2.2 Java线程的执行 7
3 线程的状态 7
3.1 JAVA线程的状态 7
3.2 改变线程状态的方法 8
3.2.1 sleep() 9
3.2.2 wait(),notify(),notifyAll() 9
3.2.3 yield() 9
3.2.4 join() 10
3.2.5 interrupt() 10
1 线程的概念
1.1 线程的概念
线程:是"进程"中某个单一顺序的执行流。是程序执行流的最小单元,一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
Java中,每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行着。一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内,它被称为主线程。一旦创建一个新的线程,就产生一个新的调用栈。
1.2 进程与线程
现代操作系统都支持多任务处理,多任务处理有两种截然不同的类型:基于进程和基于线程的。
基于进程的多任务处理:进程(Process)本质上是一个运行的程序。因此。基于进程的多任务处理就是系统可以同时运行多个应用程序。如系统上可以同时运行文本编辑器和word文档编辑器。
基于线程的多任务处理:线程(thread)本质是进程的一个执行流,是最小的执行单位。这就意味着一个程序可以同时执行多个任务功能。如一个文本编辑器可以在打印的同时格式化文本。
在多任务处理中多线程与多进程相比:进程是重量级任务,需要分配自己独立的地址空间和资源,进程之间的通信是昂贵的,受限的。线程是轻量级任务,线程之间共享相同的地址空间并其共同分享一个进程,线程间的通信时容易的,成本比较低。
多线程可以提高程序对CPU最大利用率:多线程可以高效使用CPU空闲时间,使得CPU空闲时间保持最低。如网络的数据传输速率远低于计算机的处理能力,本地文件系统资源的读写速度远低于CPU的处理能力,在这些数据传输和文件读写的过程中CPU空闲时间能够让其他线程利用起来。多线程能够使得CPU空闲时间得到充分的利用。
2 线程的创建
2.1 Java创建线程
Java提供了两种方法用来创建线程:
(1)实现Runnable接口
(2)扩展Tread类
2.1.1 实现Runnable接口
(1) 创建线程类
(2) 测试线程
(3) 测试结果
tl2 : 0
tl2 : 1
tl1 : 0
tl1 : 1
tl2 : 2
tl1 : 2
tl1 : 3
tl2 : 3
tl1 : 4
tl2 : 4
2.1.2 扩展Thread类
(1) 创建线程类
(2) 测试线程
(3) 测试结果
te2 : 0
te1 : 0
te1 : 1
te2 : 1
te1 : 2
te1 : 3
te2 : 2
te2 : 3
te1 : 4
te2 : 4
2.2 Java线程的实例化,启动,执行
2.2.1 Java线程的实例化
(1)如果是实现了java.lang.Runnable接口的类,则调用Thread的构造方法:
分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,作为 group 所引用的线程组的一员,并具有指定的堆栈尺寸stackSize。
(2)如果是扩展java.lang.Thread类的线程,则直接new即可。
2.2.1 Java线程的启动
在线程的Thread对象上调用start()方法,而不要直接调用run()或者别的方法。
在调用start()方法之后,发生了一系列动作:
1、启动新的执行线程(具有新的调用栈);
2、该线程从新状态转移到可运行状态(可见线程状态图);
3、当该线程获得机会执行时,其目标run()方法将运行。
注意:对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。
2.2.2 Java线程的执行
运行中的线程总是有名字的,名字有两个来源,一个是虚拟机自己给的名字,一个是你自己的定的名字。在没有指定线程名字的情况下,虚拟机总会为线程指定名字,并且主线程的名字总是mian,非主线程的名字不确定。线程都可以设置名字,也可以获取线程的名字,连主线程也不例外。
一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。当线程目标run()方法结束时该线程完成,线程转为死状态。一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。一个可运行的线程或死线程可以被重新启动。
线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。
3 线程的状态
public static enum Thread.State
extends Enum<Thread.State>
线程状态。线程可以处于下列状态之一:
• NEW
至今尚未启动的线程处于这种状态。
• RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。处于可运行状态的某一线程正在 Java 虚拟机中运行,但它可能正在等待操作系统中的其他资源,比如处理器。
• BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 之后再次进入同步的块/方法。
• WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。某一线程因为调用下列方法之一而处于等待状态:
1、不带超时值的 Object.wait
2、不带超时值的 Thread.join
3、LockSupport.park
处于等待状态的线程正等待另一个线程,以执行特定操作。 例如,已经在某一对象上调用了 Object.wait() 的线程正等待另一个线程,以便在该对象上调用 Object.notify() 或 Object.notifyAll()。已经调用了 Thread.join() 的线程正在等待指定线程终止。
• TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:
1、Thread.sleep
2、带有超时值的 Object.wait
3、带有超时值的 Thread.join
4、LockSupport.parkNanos
5、LockSupport.parkUntil
• TERMINATED
已退出的线程处于这种状态。
在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。
获取java线程状态,可以通过Thread的getState()方法
Thread.State getState() 返回该线程的状态。
3.1 Java中线程的状态
Java中线程的状态如下图:
• 新状态:线程对象已经创建,还没有在其上调用start()方法。此时它和其它的java对象一样,仅仅在堆中被分配了内存。
• 可运行状态:当一个线程创建了以后,其他的线程调用了它的start()方法,但调度程序还没有把它选定为运行线程时线程所处的状态。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也可以返回到可运行状态。
• 运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。处于这个状态的线程占用CPU,执行程序run()方法的代码。
• 等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
阻塞状态分为三种情况:
(1) 等待状态的线程位于对象等待池中:当线程运行时,如果执行了某个对象的wait()方法,JVM就会把线程放到这个对象的等待池中。
(2) 阻塞状态的线程位于对象锁池中:当线程处于运行状态时,试图获取某个对象的同步锁,如果该对象的同步锁已经被其他的线程占用,JVM就会把这个线程放到对象的锁池中。
(3) 睡眠状态的线程位于对象锁池中:当前线程执行了sleep()方法,当前线程就会让出CUP的使用,目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会。
线程阻塞的意思是:“雇主对雇工说:你守在这里,其它人做完到你的时候你就接着干活”。
线程等待的意思是:“雇主对雇工说:你去休息,用的着你的时候我会叫你,你在接着干活”。
线程睡眠的意思是:“雇主对雇工说:你去睡觉,某个时刻过来报到,然活接着干活”。
• 死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
3.2 改变线程状态的方法
从操作系统的角度讲,OS会维护一个ready queue(就绪的线程队列)。并且在某一时刻cpu只为ready queue中位于队列头部的线程服务。
在java中提供Thread类的一些方法来控制线程,常用的有sleep(),wait(),yield(),jion()等方法。
当前正在被服务的线程需要睡一会,醒来后继续被服务,这就是sleep。
当前正在被服务的线程可能觉得cpu的服务不够好,于是推出了,到等待池中休息,这就是wait。
当前正在被服务的线程可能觉得cpu的服务不够好,于是提前退出,到ready queue队列中继续排队,这就是yield。
当前正在被服务的线程被cpu服务完后需要cpu接着服务另一个线程,与是把另一个线程归并到该线程的后面候着一起服务,当然服务这个线程也要经过ready queue队列,这就是join。
线程退出最好自己实现,在运行状态中一直检验一个状态,如果这个状态为真,就一直运行,如果外界更改了这个状态变量,那么线程就停止运行。
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
wait()会使线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。
wait() 和notify()因为会对对象的“锁标志”进行操作,所以它们必须在 synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non- synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。
3.2.1 sleep()
Thread中的sleep方法:
static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
static void sleep(long millis, int nanos)
在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
线程睡眠是帮助所有线程获得运行机会的最好方法。线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。sleep()是静态方法,只能控制当前正在运行的线程。
由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁 没有被释放,其他线程仍然无法访问这个对象。sleep()方法不需要在同步的代码块中执行。但是sleep()可以通过interrupt()方法打断 线程的暂停状态,从而使线程立刻抛出InterruptedException。
3.2.2 wait(),notify(),notifyAll()
Thread从Object对象中继续了wait(),notify(),notifyAll()方法。
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void wait(long timeout)
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
void wait(long timeout, int nanos)
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
void notify()
唤醒在此对象监视器上等待的单个线程。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。wait()必须在同步的代码块中执行。 当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁,可以允许其它的线程执行一些同步操作。但是 wait()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
wait 可以让同步方法或者同步块暂时放弃对象锁,而将它暂时让给其它需要对象锁的人(这里应该是程序块,或线程)用,这意味着可在执行wait()期间调用线程 对象中的其他同步方法!在其它情况下(sleep啊,suspend啊),这是不可能的.但是注意我前面说的,只是暂时放弃对象锁,暂时给其它线程使用, 我wait所在的线程还是要把这个对象锁收回来的呀.wait什么?就是wait别人用完了还给我啊!
好,那怎么把对象锁收回来呢?
第一种方法,限定借出去的时间.在wait()中设置参数,比如wait(1000),以毫秒为单位,就表明我只借出去1秒中,一秒钟之后,我自动收回.
第二种方法,让借出去的人通知我,他用完了,要还给我了.这时,我马上就收回来.哎,假如我设了1小时之后收回,别人只用了半小时就完了,那怎么办呢?靠!当然用完了就收回了,还管我设的是多长时间啊.
那么别人怎么通知我呢?相信大家都可以想到了,notify(),这就是最后一句话"而且只有在一个notify()或notifyAll()发生变化的时候,线程才会被唤醒"的意思了. notify()唤醒在此对象监视器上等待的单个线程。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程将等待从锁池中获得机锁,然后回到wait()前的中断现场。 notifyAll()唤醒在此对象监视器上等待的所有线程。
3.2.3 yield()
Thread中的yield方法
static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
Yield()方法是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。
yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果,因为让步的线程还有可能被线程调度程序再次选中
3.2.4 join()
Thread中的join方法
void join()
等待该线程终止。
void join(long millis)
等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos)
等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作,实现线程串行化执行。
join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。值得注意的是,线程的在被激活后不一定马上就运行,而是进入到可运行线程的 队列中。但是join()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
如t.join(5000);当前线程运行完后,则让t线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态
3.2.5 interrupt()
Thread中的interrupt(),interrupted()方法
void interrupt()
中断线程。
static boolean interrupted()
测试当前线程是否已经中断。
interrupt()中断线程。需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛 出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但 是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。
3.3 Java线程的优先级
线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,高优先级的线程比低优先级的线程有更高的几率得到执行。但这仅仅是大多数情况。
当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。
3.3.1 线程优先级的值
线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:
static int MAX_PRIORITY 线程可以具有的最高优先级。
static int MIN_PRIORITY 线程可以具有的最低优先级。
static int NORM_PRIORITY 分配给线程的默认优先级。
3.3.2 设置线程优先级
Thread中获取线程优先级getPriority(),和设置线程优先级setPriority()。
int getPriority()
返回线程的优先级。
void setPriority(int newPriority)
更改线程的优先级。
可以通过setPriority方法更改优先级。优先级不能超出1-10的取值范围,否则抛出 IllegalArgumentException。另外如果该线程已经属于一个线程组(ThreadGroup),该线程的优先级不能超过该线程组的优先级。
4 线程的同步
5 线程的通信
/*********************
未完待续
目录
1 线程的概念 1
1.1 线程的概念 1
1.2 进程与线程 1
2 线程的创建 2
2.1 JAVA创建线程 2
2.1.1 实现Runnable接口 2
2.1.2 扩展Thread类 4
2.2 JAVA线程的实例化,启动,执行 6
2.2.1 Java线程的实例化 6
2.2.1 Java线程的启动 7
2.2.2 Java线程的执行 7
3 线程的状态 7
3.1 JAVA线程的状态 7
3.2 改变线程状态的方法 8
3.2.1 sleep() 9
3.2.2 wait(),notify(),notifyAll() 9
3.2.3 yield() 9
3.2.4 join() 10
3.2.5 interrupt() 10
1 线程的概念
1.1 线程的概念
线程:是"进程"中某个单一顺序的执行流。是程序执行流的最小单元,一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
Java中,每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行着。一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内,它被称为主线程。一旦创建一个新的线程,就产生一个新的调用栈。
1.2 进程与线程
现代操作系统都支持多任务处理,多任务处理有两种截然不同的类型:基于进程和基于线程的。
基于进程的多任务处理:进程(Process)本质上是一个运行的程序。因此。基于进程的多任务处理就是系统可以同时运行多个应用程序。如系统上可以同时运行文本编辑器和word文档编辑器。
基于线程的多任务处理:线程(thread)本质是进程的一个执行流,是最小的执行单位。这就意味着一个程序可以同时执行多个任务功能。如一个文本编辑器可以在打印的同时格式化文本。
在多任务处理中多线程与多进程相比:进程是重量级任务,需要分配自己独立的地址空间和资源,进程之间的通信是昂贵的,受限的。线程是轻量级任务,线程之间共享相同的地址空间并其共同分享一个进程,线程间的通信时容易的,成本比较低。
多线程可以提高程序对CPU最大利用率:多线程可以高效使用CPU空闲时间,使得CPU空闲时间保持最低。如网络的数据传输速率远低于计算机的处理能力,本地文件系统资源的读写速度远低于CPU的处理能力,在这些数据传输和文件读写的过程中CPU空闲时间能够让其他线程利用起来。多线程能够使得CPU空闲时间得到充分的利用。
2 线程的创建
2.1 Java创建线程
Java提供了两种方法用来创建线程:
(1)实现Runnable接口
(2)扩展Tread类
2.1.1 实现Runnable接口
(1) 创建线程类
package com.thread; /** * 实现Runnable接口创建线程类 * @author xmong * */ public class TestThreadImpl implements Runnable{ /** * 重写Runnable接口的run方法 * 当线程启动后会执行该方法 */ @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep((long)(Math.random()*1000)); System.out.println(Thread.currentThread().getName()+" : "+i); } catch (InterruptedException e) { e.printStackTrace(); } } } }
(2) 测试线程
package com.thread; /** * 测试多线程 * @author xmong * */ public class TestThread { /** * 创建两个线程实例 * 测试实现Runnable接口的线程 */ public void testThreadImpl(){ TestThreadImpl tl1 = new TestThreadImpl(); TestThreadImpl tl2 = new TestThreadImpl(); Thread t1 = new Thread(tl1,"tl1"); Thread t2 = new Thread(tl2,"tl2"); t1.start(); t2.start(); } /** * main方法测试 * @param args */ public static void main(String[] args) { TestThread tt = new TestThread(); tt.testThreadImpl(); } }
(3) 测试结果
tl2 : 0
tl2 : 1
tl1 : 0
tl1 : 1
tl2 : 2
tl1 : 2
tl1 : 3
tl2 : 3
tl1 : 4
tl2 : 4
2.1.2 扩展Thread类
(1) 创建线程类
package com.thread; /** * 通过扩展Thread实现线程 * @author xmong * */ public class TestThreadExt extends Thread{ /** * 构造线程 * @param name */ public TestThreadExt(String name){ super(name); } /** * 重写Thread类的run方法 * 当线程启动后会执行该方法 */ @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep((long)(Math.random()*1000)); System.out.println(this.getName()+" : "+i); } catch (InterruptedException e) { e.printStackTrace(); } } } }
(2) 测试线程
package com.thread; /** * 测试多线程 * @author xmong * */ public class TestThread { /** * 创建两个线程实例 * 测试实现Runnable接口的线程 */ public void testThreadImpl(){ TestThreadImpl tl1 = new TestThreadImpl("tl1"); TestThreadImpl tl2 = new TestThreadImpl("tl2"); Thread t1 = new Thread(tl1); Thread t2 = new Thread(tl2); t1.start(); t2.start(); } /** * 创建两个线程实例 * 测试扩展Thread类的线程 */ public void testThreadExt(){ TestThreadExt t1 = new TestThreadExt("te1"); TestThreadExt t2 = new TestThreadExt("te2"); t1.start(); t2.start(); } /** * main方法测试 * @param args */ public static void main(String[] args) { TestThread tt = new TestThread(); //tt.testThreadImpl(); tt.testThreadExt(); } }
(3) 测试结果
te2 : 0
te1 : 0
te1 : 1
te2 : 1
te1 : 2
te1 : 3
te2 : 2
te2 : 3
te1 : 4
te2 : 4
2.2 Java线程的实例化,启动,执行
2.2.1 Java线程的实例化
(1)如果是实现了java.lang.Runnable接口的类,则调用Thread的构造方法:
Thread(Runnable target) Thread(Runnable target, String name) Thread(ThreadGroup group, Runnable target) Thread(ThreadGroup group, Runnable target, String name) Thread(ThreadGroup group, Runnable target, String name, long stackSize)
分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,作为 group 所引用的线程组的一员,并具有指定的堆栈尺寸stackSize。
(2)如果是扩展java.lang.Thread类的线程,则直接new即可。
2.2.1 Java线程的启动
在线程的Thread对象上调用start()方法,而不要直接调用run()或者别的方法。
在调用start()方法之后,发生了一系列动作:
1、启动新的执行线程(具有新的调用栈);
2、该线程从新状态转移到可运行状态(可见线程状态图);
3、当该线程获得机会执行时,其目标run()方法将运行。
注意:对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。
2.2.2 Java线程的执行
运行中的线程总是有名字的,名字有两个来源,一个是虚拟机自己给的名字,一个是你自己的定的名字。在没有指定线程名字的情况下,虚拟机总会为线程指定名字,并且主线程的名字总是mian,非主线程的名字不确定。线程都可以设置名字,也可以获取线程的名字,连主线程也不例外。
一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。当线程目标run()方法结束时该线程完成,线程转为死状态。一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。一个可运行的线程或死线程可以被重新启动。
线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。
3 线程的状态
public static enum Thread.State
extends Enum<Thread.State>
线程状态。线程可以处于下列状态之一:
• NEW
至今尚未启动的线程处于这种状态。
• RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。处于可运行状态的某一线程正在 Java 虚拟机中运行,但它可能正在等待操作系统中的其他资源,比如处理器。
• BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 之后再次进入同步的块/方法。
• WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。某一线程因为调用下列方法之一而处于等待状态:
1、不带超时值的 Object.wait
2、不带超时值的 Thread.join
3、LockSupport.park
处于等待状态的线程正等待另一个线程,以执行特定操作。 例如,已经在某一对象上调用了 Object.wait() 的线程正等待另一个线程,以便在该对象上调用 Object.notify() 或 Object.notifyAll()。已经调用了 Thread.join() 的线程正在等待指定线程终止。
• TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:
1、Thread.sleep
2、带有超时值的 Object.wait
3、带有超时值的 Thread.join
4、LockSupport.parkNanos
5、LockSupport.parkUntil
• TERMINATED
已退出的线程处于这种状态。
在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。
获取java线程状态,可以通过Thread的getState()方法
Thread.State getState() 返回该线程的状态。
3.1 Java中线程的状态
Java中线程的状态如下图:
• 新状态:线程对象已经创建,还没有在其上调用start()方法。此时它和其它的java对象一样,仅仅在堆中被分配了内存。
• 可运行状态:当一个线程创建了以后,其他的线程调用了它的start()方法,但调度程序还没有把它选定为运行线程时线程所处的状态。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也可以返回到可运行状态。
• 运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。处于这个状态的线程占用CPU,执行程序run()方法的代码。
• 等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
阻塞状态分为三种情况:
(1) 等待状态的线程位于对象等待池中:当线程运行时,如果执行了某个对象的wait()方法,JVM就会把线程放到这个对象的等待池中。
(2) 阻塞状态的线程位于对象锁池中:当线程处于运行状态时,试图获取某个对象的同步锁,如果该对象的同步锁已经被其他的线程占用,JVM就会把这个线程放到对象的锁池中。
(3) 睡眠状态的线程位于对象锁池中:当前线程执行了sleep()方法,当前线程就会让出CUP的使用,目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会。
线程阻塞的意思是:“雇主对雇工说:你守在这里,其它人做完到你的时候你就接着干活”。
线程等待的意思是:“雇主对雇工说:你去休息,用的着你的时候我会叫你,你在接着干活”。
线程睡眠的意思是:“雇主对雇工说:你去睡觉,某个时刻过来报到,然活接着干活”。
• 死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
3.2 改变线程状态的方法
从操作系统的角度讲,OS会维护一个ready queue(就绪的线程队列)。并且在某一时刻cpu只为ready queue中位于队列头部的线程服务。
在java中提供Thread类的一些方法来控制线程,常用的有sleep(),wait(),yield(),jion()等方法。
当前正在被服务的线程需要睡一会,醒来后继续被服务,这就是sleep。
当前正在被服务的线程可能觉得cpu的服务不够好,于是推出了,到等待池中休息,这就是wait。
当前正在被服务的线程可能觉得cpu的服务不够好,于是提前退出,到ready queue队列中继续排队,这就是yield。
当前正在被服务的线程被cpu服务完后需要cpu接着服务另一个线程,与是把另一个线程归并到该线程的后面候着一起服务,当然服务这个线程也要经过ready queue队列,这就是join。
线程退出最好自己实现,在运行状态中一直检验一个状态,如果这个状态为真,就一直运行,如果外界更改了这个状态变量,那么线程就停止运行。
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
wait()会使线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。
wait() 和notify()因为会对对象的“锁标志”进行操作,所以它们必须在 synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non- synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。
3.2.1 sleep()
Thread中的sleep方法:
static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
static void sleep(long millis, int nanos)
在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
线程睡眠是帮助所有线程获得运行机会的最好方法。线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。sleep()是静态方法,只能控制当前正在运行的线程。
由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁 没有被释放,其他线程仍然无法访问这个对象。sleep()方法不需要在同步的代码块中执行。但是sleep()可以通过interrupt()方法打断 线程的暂停状态,从而使线程立刻抛出InterruptedException。
3.2.2 wait(),notify(),notifyAll()
Thread从Object对象中继续了wait(),notify(),notifyAll()方法。
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void wait(long timeout)
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
void wait(long timeout, int nanos)
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
void notify()
唤醒在此对象监视器上等待的单个线程。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。wait()必须在同步的代码块中执行。 当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁,可以允许其它的线程执行一些同步操作。但是 wait()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
wait 可以让同步方法或者同步块暂时放弃对象锁,而将它暂时让给其它需要对象锁的人(这里应该是程序块,或线程)用,这意味着可在执行wait()期间调用线程 对象中的其他同步方法!在其它情况下(sleep啊,suspend啊),这是不可能的.但是注意我前面说的,只是暂时放弃对象锁,暂时给其它线程使用, 我wait所在的线程还是要把这个对象锁收回来的呀.wait什么?就是wait别人用完了还给我啊!
好,那怎么把对象锁收回来呢?
第一种方法,限定借出去的时间.在wait()中设置参数,比如wait(1000),以毫秒为单位,就表明我只借出去1秒中,一秒钟之后,我自动收回.
第二种方法,让借出去的人通知我,他用完了,要还给我了.这时,我马上就收回来.哎,假如我设了1小时之后收回,别人只用了半小时就完了,那怎么办呢?靠!当然用完了就收回了,还管我设的是多长时间啊.
那么别人怎么通知我呢?相信大家都可以想到了,notify(),这就是最后一句话"而且只有在一个notify()或notifyAll()发生变化的时候,线程才会被唤醒"的意思了. notify()唤醒在此对象监视器上等待的单个线程。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程将等待从锁池中获得机锁,然后回到wait()前的中断现场。 notifyAll()唤醒在此对象监视器上等待的所有线程。
3.2.3 yield()
Thread中的yield方法
static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
Yield()方法是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。
yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果,因为让步的线程还有可能被线程调度程序再次选中
3.2.4 join()
Thread中的join方法
void join()
等待该线程终止。
void join(long millis)
等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos)
等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作,实现线程串行化执行。
join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。值得注意的是,线程的在被激活后不一定马上就运行,而是进入到可运行线程的 队列中。但是join()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
如t.join(5000);当前线程运行完后,则让t线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态
3.2.5 interrupt()
Thread中的interrupt(),interrupted()方法
void interrupt()
中断线程。
static boolean interrupted()
测试当前线程是否已经中断。
interrupt()中断线程。需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛 出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但 是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。
3.3 Java线程的优先级
线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,高优先级的线程比低优先级的线程有更高的几率得到执行。但这仅仅是大多数情况。
当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。
3.3.1 线程优先级的值
线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:
static int MAX_PRIORITY 线程可以具有的最高优先级。
static int MIN_PRIORITY 线程可以具有的最低优先级。
static int NORM_PRIORITY 分配给线程的默认优先级。
3.3.2 设置线程优先级
Thread中获取线程优先级getPriority(),和设置线程优先级setPriority()。
int getPriority()
返回线程的优先级。
void setPriority(int newPriority)
更改线程的优先级。
可以通过setPriority方法更改优先级。优先级不能超出1-10的取值范围,否则抛出 IllegalArgumentException。另外如果该线程已经属于一个线程组(ThreadGroup),该线程的优先级不能超过该线程组的优先级。
4 线程的同步
5 线程的通信
/*********************
未完待续
发表评论
-
Java validation(java验证器实现)
2014-03-18 11:45 3732Java validation 1. java验证器 在 ... -
Memo class备注类信息
2014-03-18 09:52 910Memo Class 1. 什么是Memo Class Mem ... -
java annotation注解
2014-01-24 18:01 9571. Annotation的声明方式 An ... -
Java RMI
2013-03-28 15:12 1745Java Rmi 目录 1 JAVA RMI 1 ... -
java内部类
2013-03-19 16:25 1068Java内部类 目录 1 JAVA ... -
java多线程设计模式之订单模式
2013-03-11 14:00 2717Java多线程实现订单模式: 客户端线程向服务端发起请求后, ... -
java多线程设计模式之线程池处理请求
2013-03-08 17:50 1829Java实现线程池处理请求: 客户端线程发出请求,请求存入请 ... -
java多线程设计模式之异步处理请求
2013-03-08 12:36 4530Java实现多线程异步处理请求: Java实现多线程异步处理 ... -
java多线程设计模式之读写文件模式
2013-03-07 17:56 1608Java实现多线程读写数据 ... -
java多线程设计模式之生产者与消费者
2013-03-07 11:34 1081Java实现多线程生产者与消费者: 生产者线程负责生产产品 ... -
java多线程设计模式之文件保存
2013-03-06 16:16 1632Java实现多线程保存文件:两线程去保存文件,一个保存线程定时 ... -
java多线程设计模式之队列通信
2013-03-06 13:51 2519Java实现多线程处理队列请求通信:客户端线程向请求队列中不断 ... -
Java读linux系统文件文件名乱码
2012-12-06 17:01 91851,问题描述 web应用想通过Java读取linux系统文件显 ... -
Java安全加密
2012-11-28 10:24 2005安全加密 目录 1 加密安全 1 1.1 应用的安全 1 ... -
图着色问题
2012-11-27 13:05 3150图着色问题 目录 1 图 ... -
JDK6新特性
2012-07-03 23:24 2906JDK6的新特性 JDK6的新特性之一_Desktop类 ... -
JDK7新特性
2012-07-03 15:39 3534JDK7新特性 一 JDK7新特性简介 准备 JDK7下载 ... -
JDK5新特性
2012-07-03 10:23 73JDK5.0新特性 1.自动封箱和自动解封(简单类型和封装类 ... -
代理模式
2012-06-13 14:12 1399代理模式 目录 1 代理 ... -
java垃圾回收机制
2012-06-11 11:30 2569Java内存回收 目录 1 JAVA内存STACK和HE ...
相关推荐
Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...
### Java多线程操作数据库:深入解析与应用 在当今高度并发的应用环境中,Java多线程技术被广泛应用于处理数据库操作,以提升系统的响应速度和处理能力。本文将基于一个具体的Java多线程操作数据库的应用程序,深入...
Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...
Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,极大地提升了程序的效率和性能。在Java中,实现多线程有两种主要方式:通过实现Runnable接口或者继承Thread类。本案例将深入探讨Java多线程中的关键...
Java多线程是Java编程中的一个重要概念,它允许程序同时执行多个任务,提高了程序的效率和响应速度。在Java中,实现多线程有两种主要方式:继承Thread类和实现Runnable接口。 1. 继承Thread类: 当我们创建一个新...
Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...
### Java多线程分页查询知识点详解 #### 一、背景与需求分析 在实际的软件开发过程中,尤其是在处理大量数据时,如何高效地进行数据查询成为了一个关键问题。例如,在一个用户众多的社交平台上,当用户需要查看...
《汪文君JAVA多线程编程实战》是一本专注于Java多线程编程的实战教程,由知名讲师汪文君倾力打造。这本书旨在帮助Java开发者深入理解和熟练掌握多线程编程技术,提升软件开发的效率和质量。在Java平台中,多线程是...
java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题
Java多线程读大文件 java多线程写文件:多线程往队列中写入数据
Java多线程机制是Java编程中至关重要的一部分,它允许程序同时执行多个任务,提升应用程序的效率和响应性。以下是对各个知识点的详细说明: 9.1 Java中的线程: Java程序中的线程是在操作系统级别的线程基础上进行...
在本文中,我们将深入浅出Java多线程编程的世界,探索多线程编程的基本概念、多线程编程的优点、多线程编程的缺点、多线程编程的应用场景、多线程编程的实现方法等内容。 一、多线程编程的基本概念 多线程编程是指...
在Java编程中,多线程并发是提升程序执行效率、充分利用多核处理器资源的重要手段。本文将基于"java 多线程并发实例"这个主题,深入探讨Java中的多线程并发概念及其应用。 首先,我们要了解Java中的线程。线程是...
《JAVA多线程教学演示系统》是一篇深入探讨JAVA多线程编程的论文,它针对教育领域中的教学需求,提供了一种生动、直观的演示方式,帮助学生更好地理解和掌握多线程技术。这篇论文的核心内容可能包括以下几个方面: ...
本项目以"java多线程实现大批量数据导入源码"为题,旨在通过多线程策略将大量数据切分,并进行并行处理,以提高数据处理速度。 首先,我们需要理解Java中的线程机制。Java通过`Thread`类来创建和管理线程。每个线程...
综上所述,"java多线程查询数据库"是一个涉及多线程技术、线程池管理、并发控制、分页查询等多个方面的复杂问题。通过理解和掌握这些知识点,我们可以有效地提高数据库操作的效率和系统的响应速度。
《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...
这份“JAVA多线程编程技术PDF”是学习和掌握这一领域的经典资料,涵盖了多线程的全部知识点。 首先,多线程的核心概念包括线程的创建与启动。在Java中,可以通过实现Runnable接口或继承Thread类来创建线程。创建后...
Java多线程编程是Java开发中的重要组成部分,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,多线程主要通过继承Thread类或实现Runnable接口来实现。本教程《Java多线程编程核心技术》将...
Java多线程编程实战指南(核心篇) 高清pdf带目录 随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了――如今...