java多线程详解(一)

时间:2021-04-20 17:32:35

一、概念

1、进程:是指一个内存中运行的应用程序。

每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。

比如,在windows系统中,一个运行的exe就是一个进程。

 

2、线程:是进程中的一个执行流程,一个进程中可以运行多个线程。

比如:java.exe进程中可以运行多个线程。

线程总是属于某一个进程的,进程中的多个线程共享进程的内存。

 

注:所谓的“同时”,实际上是多个线程之间轮流执行的结果,只不过每个线程执行的时间很短,就切换到另一个线程,人眼看起来像是同时在执行。

 

二、java中线程

1、使用Thread类或者Runnable接口 来定义、实例化和启动新线程。

2、一个Thread类实例是一个对象,具有变量和方法,存放在堆内存中。

3、每个线程都有一个调用栈,一旦创建一个新线程,就会产生一个新的调用栈。

 

线程的创建和启动

一、实例化线程

a)        扩展java.lang.Thread类,如:

package com;

public class MyThread extends Thread {

public MyThread(String name) {
super(name) ;
}

@Override
public void run() {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 10; j++) {
System.out.println(this.getName()+" : "+i);
}
}
}

public static void main(String[] args) {
Thread t1 = new MyThread("张三") ;
Thread t2 = new MyThread("李四") ;

t1.start();
t2.start();
}
}

a)        实现java.lang.Runnable接口

package com;

public class MyRunnable implements Runnable {

private String name ;

public MyRunnable(String name) {
this.name = name ;
}

public void run() {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 10; j++) {
System.out.println(name +" : "+i);
}
}
}


public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable("张三")) ;
Thread t2 = new Thread(new MyRunnable("李四")) ;
t1.start();
t2.start();
}
}

一、启动线程

a)        在线程的Thread方法上,调用start()方法,不是run()方法

b)        在调用start()方法前:

                        i.             线程处于新状态,新状态指有一个Thread对象,但还并不是一个真正的线程。

c)        在调用start()方法后:

                        i.             发生如下变化:

1.        启动新的执行线程(具有新的调用栈)

2.        该状态从新状态转移到可执行状态

3.        当该线程获得机会执行时,其目标run()方法将运行

注:对于java来说,run()方法就像main()方法一样,只是新线程知道调用的方法名称和签名,

因此,在Runnable上或者Thread上调用run方法是合法的,但并不启动新的线程。

 

二、线程模型和线程调度原理

a)        线程栈模型

                        i.             线程模型是理解线程调度原理以及线程执行过程的基础。

                      ii.             线程栈:是指某一个时刻内存中线程调度的栈信息,当前正在调用的方法总是位于栈顶,线程栈中的内容是随着线程的运行状态变化而变化的,我们要研究线程栈,就要选择某一个运行时间(也就是代码运行到哪一行),如下图:

java多线程详解(一)

  i.             上图中,

1.        第一个栈A是主线程main()方法运行带System.out.pringln(“Hello Java”);时的运行栈信息,main()方法位于栈A的栈顶;

2.        第二个栈A是主线程main()方法运行到new JavaThreadDemo().threadMethod();方法时的栈信息,threadMethod()方法位于栈A的栈顶。

3.        在threadMethod()方法中,运行到start()方法时,会新建立一个线程,新建立的线程也将拥有自己的线程栈B,run()方法位于栈B的栈顶。

4.        此时,线程栈A和线程栈B并行运行

5.        由此可以看出,方法调用和线程启动的区别:

a)        方法调用只是在原来的线程栈中调用方法即可。

b)        而线程启动会新建立一个独立的线程栈来运行自己的线程。

b)        线程的生命周期

线程的生命周期包括5中状态:新建、可运行状态、运行状态、阻塞、死亡。

java多线程详解(一)

i.  新建:创建了一个新的线程对象,但是还没有调用线程的start()方法,此时此案成处于新建状态,如:Thread t1 = new MyThread("张三") ;

ii. 可运行状态:调用线程的start()方法,线程进入可运行状态,此时线程等待JVM的调度程序将其变为运行状态

iii. 运行状态:线程调度程序,从众多的可运行状态线程中,选择一个线程来执行。这也是线程进入运行状态的唯一方式,必须有JVM来调度。

iv. 阻塞状态:线程的等待、睡眠和阻塞统称为阻塞状态,此时线程依然是活着的,处于待命状态,知道某个条件出现时,变为可运行状态。

v. 死亡状态:当线程的run()方法执行完毕后,线程结束。此时线程已经不存在,它所占用的所有资源都会被回收。

二、线程阻塞:

a)        线程阻塞有多种,常见的有三种(IO阻塞不讨论):

     i.   睡眠:调用线程的sleep()方法进入睡眠状态。

当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。

1.        线程处于睡眠状态时,JVM调度程序会暂停此线程的执行,从而去执行其他的处于可运行状态的线程。

2.        sleep()方法是Thread的静态方法,只能控制当前线程的睡眠。

3.   线程睡眠时间到了之后,返回到可运行状态,等待JVM调度。

         ii.             等待:

        iii.             获取线程锁而阻塞

b)  让线程暂时离开运行状态的三中方法:

        i.    调用线程的sleep()方法,使线程睡眠一段时间

        ii.   调用线程的yield()方法,使线程暂时回到可运行状态,来使其他线程有机会执行。

       iii.  调用线程的join()方法,使当前线程停止执行,知道当前线程中加入的线程执行完毕后,当前线程才可以执行

 

三、线程的优先级和让步

a)        线程让步:通过Thread.yield()来实现的,yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。

b)        JVM线程调度程序是基于优先级的抢先调度机制。

c)        当线程池中线程都具有相同的优先级,调度程序的JVM*选择他喜欢的线程;如果存在不同级别的优先级,JVM会有高概率选择级别高的线程,但并不一定选择高的,只是概率大一点而已。

d)        如果是线程池中具有相同的优先级,这个时候调度程序的操作有两种可能:

                        i.             选择一个线程运行,知道他阻塞或者运行完成为止;

                      ii.             时间分开,为池内的每个线程提供均匀的运行机会

e)        可以通过setPriority(int newPriority),更改线程的优先级。如:

Thread t = new MyThread();
       t.setPriority(8);
       t.start();

四、小结:

a)        到目前为止,我们知道了,线程离开运行状态共有3中方法:

                        i.             调用Thread.sleep():是当前线程睡眠n毫秒以后,进入可执行状态,等待调度;

                      ii.             调用Thread.yield(): 让当前运行线程回到可运行状态,是的具有相同优先级的线程有机会执行;

                     iii.             调用join()方法:保证当前线程停止运行,直到该线程所加入的线程完成为止,然而,如果它加入的线程没有存活,则当前线程不需要停止。

b)        除了以上三种方式外,还有下面几种特殊情况可能是线程离开运行状态

                        i.             线程的run()方法完成;

                      ii.             在对象上调用wait()方法(不是在线程上);

                     iii.             线程不能再对象上获得锁定,他正试图运行该对象的方法代码;

                     iv.             线程调度程序可以决定将当前运行状态移动到可运行状态,以便让另一个线程获得运行机会,而不需要任何理由。


参考文章:http://www.cnblogs.com/riskyer/p/3263032.html

http://www.2cto.com/kf/201409/335651.html

http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html