一、线程概念

现代的操作系统都是多用户多进程分时操作系统,所以我们在使用操作系统时,可以一边听歌,一边下载,还可以聊天等等,事实上我们的操作系统同时还运行着很多后台进程,你可以打开window系统的任务管理器就可以看到很多进程在运行。

48、线程概念和两种实现方法

在往下学习之前,让我们先弄清楚什么是程序,进程和线程这几个概念。

1、程序:利用编程语言开发的一个工具软件, 静态的,在没有启动运行之前只是磁盘中的一个普通文件。

2、进程:程序启动之后就变成了进程,进程是在内存中运行的,具有一定的生命周期,如果程序运行解析,进程在内存中就会回收,生命周期也就结束了,当然程序是可以再次运行,重写变成进程。

3、线程:进程在运行过程中的执行走向,线索。线程是比进程更小的一个单位,一个进程可以有一个或多个线程的。

java程序启动运行时,就自动产生了一个线程,主函数main就是在这个线程上运行的,当不再产生新的线程时,程序就是单线程。到目前为止我们编写的所有的java程序都是单线程的,接下来我们要学习如何实现多线程的java程序。

二、线程的实现方法

1、单线程。我们新建一个类Stu1实现一个简单的循环。

1

2

3

4

5

6

7

8

9

10

11

  public class Stu1 {

 

    public static void main(String[] args) {

        //主线程

        for(int i=0;i<10;i++){

            System.out.println("主线程:"+i);

        }

        System.out.println("主线程:死亡");

    }

     

}

运行上面代码,程序只有一个线程,运行完毕程序也就结束了。

2、通过继承Thread类实现第一个线程。新建一个Thread1类继承Thread类,并重写run方法,run方法就是线程的执行内容。实现代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/**

 * 第一种方式

 * 1、继承 Thread

 * 2、重写run方法

 * 3、在主线程中通过start方法启动线程

 * @author Administrator

 *

 */

public class Thread1 extends Thread{

    //线程体

    public void run(){

        for(int i=0;i<5;i++){

            System.out.println("线程1:"+i);

        }

        System.out.println("线程1:死亡");

    }

}

我们要启动这个线程不是调用run()方法,而是使用start()方法启动线程,线程启动后会自动执行run()方法。

修改上面的Stu1的main方法,启动这个线程。修改如下:

1

2

3

4

5

6

7

8

9

10

public static void main(String[] args) {

        Thread1 thread1=new Thread1();

        thread1.start();//通过start启动线程,他会自动执行run方法

 

        //主线程

        for(int i=0;i<5;i++){

            System.out.println("主线程:"+i);

        }

        System.out.println("主线程:死亡");  

    }

现在你可以再次运行这个段代码,查看后台的输出。你可以多运行几次,我的其中一次的输出如下:

主线程:0

线程1:0

主线程:1

线程1:1

主线程:2

线程1:2

主线程:3

线程1:3

主线程:4

线程1:4

主线程:死亡

线程1:死亡

由输出可见,主线程和子线程之间是独立运行的,它们将会轮流的占用CPU,而那个线程会占有CPU是由操作系统决定的。所以我们看到多次运行这个程序时,每一次的输出可能都不一样。

3、通过实现Runnable接口实现线程。为了克服java单继承的限制,java提供了另外一种实现线程类的方式,就是实现Runnable接口,因为接口是可以同时实现多个接口的。同样需要实现run方法。实现代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

 /**

 * 第二种方式

 * 1、实现Ruannable接口

 * 2、实现run方法

 * 3、在主线程通过Thread启动线程

 * @author Administrator

 *

 */

public class Thread2  implements Runnable{

 

    @Override

    public void run() {

        for(int i=0;i<5;i++){

            System.out.println("线程2:"+i);

        }

        System.out.println("线程2:死亡");

    }

 

}

启动这个线程也有点区别,你需要new一个Thread类,并把这个实现类作为参数传入这个Thread类,修改Stu1的main方法如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public static void main(String[] args) {

     

        Thread1 thread1=new Thread1();

        thread1.start();//通过start启动线程,他会自动执行run方法

         

        //启动另外一个线程,该线程实现了Ruannable接口

        Thread t2=new Thread(new Thread2());

        t2.start();

         

        //主线程

        for(int i=0;i<10;i++){

            System.out.println("主线程:"+i);

        }

        System.out.println("主线程:死亡");

         

    }

再次运行该程序,现在程序共有三个线程,每个线程还是独立的,所以输出的结果感觉也是错乱的。

4、其他的方法。事实上面我们可以通过setPriority设置优先级别,当然设置优先级别也只是一个给操作系统一个建议,最后谁先占用CPU还是按照操作系统自己的算法。

另外我们也可以通过sleep()方法让线程休眠,这样他就不会占用CPU了。参考代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

public static void main(String[] args) {

 

    Thread1 thread1 = new Thread1();

    thread1.setPriority(10);

    thread1.start();// 通过start启动线程,他会自动执行run方法

 

    Thread t2 = new Thread(new Thread2());

    t2.start();

 

    //主线程休眠

    try {

        Thread.currentThread().sleep(2000);

    catch (InterruptedException e) {

        // TODO Auto-generated catch block

        e.printStackTrace();

    }

 

    // 主线程

    for (int i = 0; i < 10; i++) {

        System.out.println("主线程:" + i);

    }

    System.out.println("主线程:死亡");

 

}

运行上面的代码,我们发现主线程的循环要等待两秒后才输出,而线程1的优先级别是最高的。

三、java线程的状态

每个线程都有一个从初始状态到死亡状态的生命周期,其间根据业务流程经过就绪状态(runnable)、运行状态(runing)、阻塞状态(blacked),这些状态可以根据流行需要相互转换。线程状态转换示意图

48、线程概念和两种实现方法

0、初始状态跟其他Java对象一样用new语句创建的线程对象处于初始状态,此时仅被分配了内存。

1、就绪状态(Runnable)

当每个线程对象创建好后,通过调用其start()方法使得该线程就进入就绪状态,就绪状态的线程进入到JVM的可运行池中等待CPU的使用权,每个线程都可以通过设置其优先级以提高取得CPU的使用权的可能性。当然,CPU的使用权跟计算机的硬件分不开的,这体现在单CPU环境下跟多CPU(及单CPU多核)下处理是不一样的,因为同一时刻可以让几个线程占用不同的CPU。

2、运行状态

在JVM中就绪状态的线程取得CPU的使用权后,得以执行run()方法中定义的程序代码。只有处于就绪状态的线程才有机会转到运行状态。

3、阻塞状态(Blocked)

运行中的线程可以根据需要放弃占用的CPU而暂停运行,这是线程就处于阻塞状态,此时Jvm不再尝试给线程分配CPU使用机会。处于阻塞状态有三种情况:

1.位于Jvm线程等待池中的阻塞状态(Blocked in object’s wait pool):当线程处于运行状态,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中,参见第20章的20.1.1节(同步代码块)。

2.位于对象锁池中的阻塞状态(Blocked in object’s lock pool):当线程处于运行状态,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中,参见第20章的20.2节(线程通信)。

3.其他阻塞状态(Otherwise Blocked):当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求,就会进入这个状态。

4、死亡状态(Deaded)

当线程正常执行完run()方法中的代码,或者遇到异常未捕获造成中断,线程就会退出运行状态,转入死亡状态,结束其生命周期。

相关文章: