浅谈线程

Posted 丁国华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈线程相关的知识,希望对你有一定的参考价值。

        在java程序设计语言中,并发程序主要集中于线程,随着越来越多的计算机系统拥有多个处理器或带有多个执行内核,线程的系统能力也得到了极大的增强,多线程编程是提高应用程序性能的重要手段。

        刚开始接触线程的概念,是从学习操作系统开始的,把一个用户的一个计算问题或者一个应用问题作为一个进程,把该进程中可以并发执行的各部分分别作为线程,随着学习的深入,慢慢揭开线程的面纱,那么线程的概念是如何定义的?线程又有着怎样的属性和组成?线程的又是怎么工作的?线程和进程又有着怎样千丝万缕的关系?如何定义启动一个线程?等等,这些基础的知识都需要我们一一攻破,那么今天这篇博文呢,小编就来简单的介绍一下线程的基本知识,在网上看到一篇写的很好的关于线程的博文,分享给大家,我是一个线程!

       线程的概念

       线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
        线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。引入线程的好处有:

         a、创建一个新线程花费的时间少;

         b、两个线程在同一个进程中的切换时间少;

         c、由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核,线程能独立执行,能充分利用和发挥处理机和外围设备并行工作的能力。

        线程的属性

        通常是在一个进程中包含多个线程,每个线程都是作为利用cpu的基本单位,是花费最小开销的实体,线程具有如下属性:

         a、同一个进程中的各线程驻留在分配给进程的主存地址空间中,且共享该进程的所有资源;

         b、一个线程被创建后便开始了她的生命周期,直到执行结束而终止;

         c、线程是处理器的独立调度单位,多个线程可以并发执行;

         d、不同的线程可以执行相同的程序;

         简单的四句话总结就是轻型实体、独立调度和分配的基本单位、可并发执行、共享进程资源。

         线程的工作原理

         线程有两个基本类型:

         a、用户级线程:管理过程全部由用户程序完成,操作系统内核只对进程进行管理。

         b、系统级线程:又叫核心级线程,由操作系统内核进行管理,操作系统内核给应用程序提供相应的系统调用和应用程序接口,以使用户程序可以创建、执行、撤销线程。

         线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程不用有系统资源,只有运行必需的一些数据结构,她与父进程的其她线程共享该进程所拥有的全部资源,线程可以创建和撤销线程,从而实现程序的并行执行,一般,线程具有就绪、阻塞和运行三种基本状态。

         在多中央处理器的系统中,不同线程可以同时在不同的中央处理器上运行,甚至当他们属于同一个进程时也是如此,大多数支持多处理器的操作系统都提供编程接口来让进程可以控制自己的额线程与各处理器之间的关联度。有时候,线程也成为轻量级进程,就像进程一样,线程在程序中是独立的,并发的执行路径,每个线程有她自己的堆栈、自己的程序计数器和自己的局部变量,但是,与分隔的进程相比,进程中的线程之间的隔离程度要小,她们共享内存、文件句柄和其他每个进程应用的状态。进程可以支持多个线程,她们看似同时执行,但互相之间并不同步,一个进程中的多个线程共享相同的内存地址,这就意味着她们可以访问相同的变量和对象,而且她们从同一堆中分配对象,尽管这让线程之间共享信息变得更容易,但必须小心,确保她们不会妨碍同一进程中的其他线程。

         java线程工具和api看似简单,但是编写有效使用线程的复杂程序并不十分容易,因为有多个线程共存在相同的内存空间中并共享相同的变量,所以必须小心,确保线程不会互相干扰,线程有五种基本操作:

         a、派生:线程在进程内派生出来,她既可以由进程派生,也可由线程派生。

         b、阻塞:如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。

         c、激活:如果阻塞线程的事件发生,则该进程被激活并进入就绪队列。

         d、调度:选择一个就绪线程进入执行状态。

         e、结束:如果一个线程执行结束,她的寄存器上下文及堆栈内容等将被释放。介绍了这么多的理论知识,接着小编定义并启动一个线程。

        定义并启动一个线程

        创建一个Thread类的实例的应用程序,必须提供将要在那个线程中运行的代码,提供一个Runnable对象或使用Thread的子类都可以。

         a、提供一个Runnable对象

         Runnable接口定义了一个单一的方法run(),在run()方法中包含了要在线程中执行的代码,Runnable对象被传递给Thread的构造器中,使用Runnable接口创建线程,代码如下:      

public class RunnableDemo implements Runnable{
	public void run(){
	//实现Runnable接口,则必须实现该接口中的run方法
		System.out.println("这是一个线程!");
	}
	public static void main(String[]args){
		(new Thread(new RunnableDemo())).start();
	}
}
        b、Thread的子类

        Thread类已经实现了Runable接口,不过其run()方法什么也不做,应用程序可以通过子类化Thread,实现其自身的run()方法,使用Thread技术分享的子类创建线程,代码如下所示:

public class ThreadDemo extends Thread{
	public void run(){
	//重写从Thread继承过来的run方法
		System.out.println("这是一个线程!");
	}
	public static void main(String[]args){
		(new Thread(new ThreadDemo())).start();
	}
}

         分析一下上面的例子,这两个demo都是通过调用Thread.statr()方法来启动新的线程,对于第一种用法,使用了一个Runnable对象,是比较常用的一种,因为Runnable对象可以子类化一个非Thread类,第二种方法再简单的应用程序中更容易使用,但是她限制了完成异步任务的类只能从Thread类继承,推荐使用第一种方法,可以将Runnable的任务和执行此任务的线程对象分开,不仅是这种方式更加灵活。

         Thread类定义了许多用于线程管理的方法,这些方法包括静态方法,用来提供调用此方法的线程的消息,或影响调用此方法的线程的状态,还有其他的方法,可以从其他的线程调用,用于管理线程和Thread对象。接着,我们在主线程中创建一个新的实现了Runnable接口的MessageLoop线程,并等待她结束,如果MessageLoop线程的时间太长,主线程会中断她,MessageLoop线程输出一系列消息,如果在她输出所有的消息之前被中断,MessageLoop线程输出信息并退出,代码如下所示:

public class Simple Threads Demo{
	//显示消息,消息前是前线程的名字
	static void print ThreadMessage(String message){
		String threadName = Thread.currentThread().getName();
		//格式化输出线程消息
		System.out.format("%s:%s%n",threadName,message);		
	}
	//私有静态内部类,实现了Runnable接口
	private static class MessageLoop implements Runnable{
		public void run(){
			String info[] ={"消息1","消息2","消息3","消息4"};
			try{
				for (int i =0;i<info.length;i++){
				//暂停4s输出消息
					Thread.sleep(4000);
					printThreadMessage(info[i]);
				}
			}catch (InterruptedException e){
				printThreadMessage("不能正常工作.");
			}
		}
	}
	public static void main (String args[]) throws InterruptedException{
		//在终端MessageLoop线程之间延迟的毫秒数(默认是一个小时)
		long delay = 1000*60*60;
		//如果有命令行参数,那么在命令行参数中给出延迟的时间
		if (args.length>0){
			try{
				//命令行中的第一个参数解析为整数(s)
				delay=Long.parseLong(args[0])*1000;
			}catch (NumberFormatException e){
				System.err.println("参数必须是整数");
				System.exit(1);
			}
		}
		printThreadMessage("启动MessageLoop线程...");
		//获得当前系统时间
		long startTime = System.currentTimeMillis();
		//创建线程t
		Thread t =new Thread(new MessageLoop());
		//启动线程t
		t.statr();
		printThreadMessage("等待MessageLoop线程结束...");
		//循环知道MessageLoop线程推出
		while(t.isAlive()){
			printThreadMessage("继续等待...");
			//最多等待1s等待MessageLoop线程结束
			t.join(1000);
			//如果线程t运行的时间超过了delay指定的时间
			if(((System.currentTimeMillis()-statrTime)>delay)&&t.isAlive()){
				printThreadMessage("时间太久了,不再等待");
				//中断线程t
				t.interrupt();
				//main线程暂停执行,知道线程t结束为止
				t.join();
			}
		}
		printThreadMessage("messageLoop线程结束!")
		
	}
}
       线程和进程的区别

       介绍了线程,我们再来简单了解一下进程,京城是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,她是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元,主要重点来介绍一下线程和进程的区别。主要有以下四个方面的。

        a、地址空间和其他资源:进程间独立,同一个进程的各线程间共享。某进程内的线程在其他进程不可见。

        b、通信:进程间通信IPC,线程间可以直接读/写进程数据段来进行同行,需要进程同步和互斥手段的辅助,以保证数据的一致性。

        c、调度和切换:线程上下文切换比进程上下文切换要快的多。

        d、在多线程OS中,进程不是一个可执行的实体。

        小编寄语:该博文小编主要简单的介绍了线程的相关知识,从基本的概念开始介绍,到线程的工作原理,以及demo,最后介绍了一下进程和线程的区别,但是理解这些还是远远不够的,我们需要做的,就是在实际项目中多多的锻炼,多多的应用,java之旅,未完待续......         

以上是关于浅谈线程的主要内容,如果未能解决你的问题,请参考以下文章

浅谈多线程

线程浅谈

浅谈线程池(上):线程池的作用及CLR线程池

浅谈Java线程池

多线程(NSThreadNSOperationGCD)编程浅谈

Python的多线程GIL浅谈