线程概念和两种实现方法

发表时间:2017-05-09 13:27:22 浏览量( 16 ) 留言数( 0 )

一、线程概念

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

attcontent/96bf404d-92a2-43d1-8682-b4751c710228.png

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

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

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

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

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

二、线程的实现方法

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

  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、继承 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方法,启动这个线程。修改如下:

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、实现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方法如下:

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了。参考代码如下:

	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),这些状态可以根据流行需要相互转换。线程状态转换示意图

attcontent/02cce36c-8b14-4294-84a2-c2499ef9d44b.png

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()方法中的代码,或者遇到异常未捕获造成中断,线程就会退出运行状态,转入死亡状态,结束其生命周期。