标签:多线程 thread
一、定义线程
1、扩展java.lang.Thread类。
此类中有个run()方法,应该注意其用法:
public void run()
java.lang
类 Thread
java.lang.Object
java.lang.Thread
所有已实现的接口:
Runnable
public class Thread extends Object implements Runnable
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。Thread 的子类应该重写该方法。
创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。例如:
public class TestThread extends Thread
{
public TestThread(String
name) {
super(name);
}
public void run()
{
for(int i
= 0;i<5;i++){
for(long k=
0; k <100000000;k++);
System. out.println(this .getName()+"
:"+i);
}
}
public static void main(String[]
args) {
Thread t1 = new TestThread("阿三" );
Thread t2 = new TestThread("李四" );
t1.start();
t2.start();
}
}
在调用start()方法之后:发生了一系列复杂的事情
启动新的执行线程(具有新的调用栈);
该线程从新状态转移到可运行状态;
当该线程获得机会执行时,其目标run()方法将运行。
2、实现java.lang.Runnable接口。
public class ThreadTest implements Runnable{
private String name ;
public ThreadTest(String
name)
{
this.name =name;
}
@Override
public void run()
{
// TODO Auto-generated
method stub
for(int i=0;i<5;i++)
{
for (long k
= 0; k < 100000000; k++) ;
System. out.println(name + ":
" + i);
}
}
public static void main(String
args[])
{
ThreadTest test= new ThreadTest("Fish" );
ThreadTest test2= new ThreadTest("Bigfish" );
Thread testThread= new Thread(test);
Thread testThread2= new Thread(test2);
testThread.start();
testThread2.start();
}
线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。
众多可运行线程中的某一个会被选中做为当前线程。可运行线程被选择运行的顺序是没有保障的。
尽管通常采用队列形式,但这是没有保障的。队列形式是指当一个线程完成“一轮”时,它移到可运行队列的尾部等待,直到它最终排队到该队列的前端为止,它才能被再次选中。事实上,我们把它称为可运行池而不是一个可运行队列,目的是帮助认识线程并不都是以某种有保障的顺序排列唱呢个一个队列的事实。
尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。
二、线程的状态
线程有五种基本状态:新生状态,就绪状态,运行状态,阻塞状态,死亡状态。状态间关系如下图:
1、新状态:线程对象已经创建,还没有在其上调用start()方法。
2、可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。
3、运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
4、等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
5、死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
线程的睡眠方法
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。
睡眠的实现:调用静态方法。
try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。
使用睡眠方法应该注意:
1、线程睡眠是帮助所有线程获得运行机会的最好方法。
2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。
3、sleep()是静态方法,只能控制当前正在运行的线程。
线程的优先级
public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。
注意:当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。
Thread接口中的优先级 摘自JDK1.6
源码中的定义
public final static int MIN_PRIORITY =
1;
public final static int NORM_PRIORITY =
5;
public final static int MAX_PRIORITY =
10;
默认的优先级为public final static int NORM_PRIORITY =
5;
当一个父线程生成一个子线程时,子线程的优先级为父线程的,在源码中:
init()方法中有这么一句:
Thread parent = currentThread();
this. priority =
parent.getPriority();
Java程序的主线程(main方法)的优先级默认是为NORM_PRIORITY,那么后序创建的线程优先级就默认为5了
如何设置线程优先级:
Threa类源码中,有一个 setPriority 方法,源码如下:
final方法,无法被子类覆盖
优先级不能超出1-10的取值范围,否则抛出IllegalArgumentException。另外如果该线程已经属于一个线程组(ThreadGroup),该线程的优先级不能超过该线程组的优先级:
public final void setPriority (int newPriority)
{
ThreadGroup g;
checkAccess();
if (newPriority
> MAX_PRIORITY || newPriority < MIN_PRIORITY)
{
throw new IllegalArgumentException();
}
if((g
= getThreadGroup()) != null) { //得到线程组的优先级,并且不能大于线程组的优先级
if (newPriority
> g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0( priority =
newPriority); //setPriority0 为一个本地方法
}
}
private native void setPriority0 (int newPriority);
线程组的优先级问题
在Java中每个线程都属于某个线程组(ThreadGroup)。 以使用下面的指令来获得目前线程所属的线程组名称:
Thread.currentThread().getThreadGroup().getName();
每一个线程产生时,都会被归入某个线程组,视线程是在哪个线程组中产生而定。如果没有指定,则归入产生该子线程的线程的线程组中。
private ThreadGroup()
{ // called from C code
this.name = "system" ;
this.maxPriority =
Thread.MAX_PRIORITY;
this.parent = null;
}
public final int getMaxPriority()
{
return maxPriority ;
}
同样还有一个setMaxPriority方法
public final void setMaxPriority(int pri)
{
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this )
{
checkAccess();
if (pri
< Thread.MIN_PRIORITY || pri > Thread.MAX_PRIORITY)
{ //这个线程组的值必须要合法 [1,10]
return;
}
maxPriority =
(parent != null)
? Math.min(pri, parent .maxPriority )
: pri; //这一句表示,当前设置的线程组优先级需要跟父线程的优先级最大级比较,最能取两个中的最小值,限制了set方法使得线程优先级大于父线程。如果没有父线程就用该设置值
ngroupsSnapshot = ngroups;
if (groups != null)
{
groupsSnapshot = Arrays. copyOf(groups,
ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
for (int i
= 0 ; i < ngroupsSnapshot ; i++) { //由于用户组的最大优先级变了,所有属于这个组的其他子线程的最大优先级也需要进行相应的改变,也就是说,仅仅能改变最大优先级,而不能改变已经创建子线程的实际优先级
groupsSnapshot[i].setMaxPriority(pri);
}
}
线程组最大优先级总结:
线程组最大优先级的设定:
- 系统线程组的最大优先级默认为Thread.MAX_PRIORITY
- 创建线程组的时候其最大优先级默认为父线程组(如果未指定父线程组,则其父线程组默认为当前线程所属线程组)的最大优先级
- 可以通过setMaxPriority更改最大优先级,但无法超过父线程组的最大优先级
setMaxPriority的问题:
- 该方法只能更改本线程组及其子线程组(递归)的最大优先级。
- 但不能影响已经创建的直接或间接属于该线程组的线程的优先级,也就是说,即使目前有一个子线程的优先级比新设定的线程组优先级大,也不会更改该子线程的优先级。只有当试图改变子线程的优先级或者创建新的子线程的时候,线程组的最大优先级才起作用。
线程的yield()方法
public static native void yield();
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
yield()方法测试:
package Threadtest;
public class yieldTest
{
public static void main(String[]
args) {
Thread t1 = new MyThread1();
Thread t2 = new Thread(new MyRunnable());
t2.start();
t1.start();
}
}
class MyThread1 extends Thread
{
public void run()
{
for (int i
= 0; i < 10; i++) {
System. out.println("线程1第" +
i + "次执行!" );
}
}
}
class MyRunnable implements Runnable
{
public void run()
{
for (int i
= 0; i < 10; i++) {
System. out.println("线程2第" +
i + "次执行!" );
Thread. yield();
}
}
}
测试结果如下:
线程2第0次执行!
线程2第1次执行!
线程2第2次执行!
线程2第3次执行!
线程1第0次执行!
线程1第1次执行!
线程1第2次执行!
线程1第3次执行!
线程1第4次执行!
线程1第5次执行!
线程1第6次执行!
线程1第7次执行!
线程1第8次执行!
线程1第9次执行!
线程2第4次执行!
线程2第5次执行!
线程2第6次执行!
线程2第7次执行!
线程2第8次执行!
线程2第9次执行!
线程的join方法()
用途:
主线程生成并起动了子线程,而子线程里要进行大量的耗时的运算(这里可以借鉴下线程的作用),当主线程处理完其他的事务后,需要用到子线程的处理结果,这个时候就要用到join();方法了。
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
举个例子:
A线程为主线程 main
B线程为main中生成的子线程
main()
{
B.start();
B.join();
sysout(n);//n为B中处理的数据,很明显 ,sysout为main()线程的东西,所以调用sysout时,需要B处理完才调用sysout
}
查看jdk中源码
public final void join () throws InterruptedException
{ //默认的是join(0)表示等待时间为0
join(0);
}
/**
Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
永远等待,直达t结束*/
throws InterruptedException
{
long base
= System.currentTimeMillis();
long now
= 0;
if (millis
< 0) {
throw new IllegalArgumentException("timeout
value is negative");
}
if (millis
== 0) {
while (isAlive())
{ //线程启动了 join地方法才有用,即需要在start()方法后面调用 join()
wait(0);
}
} else {
while (isAlive())
{
long delay
= millis - now;
if (delay
<= 0) {
break;
}
wait(delay);
now = System. currentTimeMillis() - base;
}
}
}
Java 多线程编程之多线程定义和多线程状态
标签:多线程 thread
原文地址:http://blog.csdn.net/yujin753/article/details/45267709