java 进程和线程介绍(Thread,Runnable)

一 、进程的介绍
1.1  进程的概念
每个独立执行的程序称为进程
进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程。多进程操作系统能同时运行多个进程(程序),由于CPU具备分时机制,所以每个进程都能循环获得自己的CPU时间片。由于CPU执行速度非常快,使得所有程序好象是在“同时”运行一样。
在操作系统中进程是进行系统资源分配、调度和管理的最小单位,进程在执行过程中拥有独立的内存单元。比如:Windows采用进程作为最小隔离单位,每个进程都有自己的数据段、代码段,并且与别的进程没有任何关系。因此进程间进行信息交互比较麻烦。

1.2  线程的概念

        为了解决进程调度资源的浪费,为了能够共享资源,出现了线程。线程是进程调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源,多个线程共享内存,从而极大地提高了程序的运行效率。线程是比进程更小的执行单位,线程是进程内部单一的一个顺序控制流。所谓多线程是指一个进程在执行过程中可以产生多个线程,这些线程可以同时存在、同时运行,形成多条执行线索。
一个进程可能包含了多个同时执行的线程。
一个或更多的线程构成了一个进程(操作系统是以进程为单位的,而进程是以线程为单位的,进程中必须有一个主线程 main Thread)。

如果一个进程没有了,那么线程肯定会消失,但是如果某个线程消失了,进程未必会消失。只有所有的线程都结束了,进程才会结束!!!而且所有线程都是在进程的基础之上同时运行。

1.3 进程和线程的关系以及区别

        多线程是实现并发机制的一种有效手段。进程和线程一样,都是实现并发的一个基本单位。线程和进程的主要差别体现在以下两个方面:

1. 同样作为基本的执行单元,线程是划分得比进程更小的执行单位。
2. 每个进程都有一段专用的内存区域。与此相反,线程却共享内存单元(包括代码和数据),通过共享的内存单元来实现数据交换、实时通信与必要的同步操作。

        多线程的应用范围很广。在一般情况下,程序的某些部分同特定的事件或资源联系在一起,同时又不想为它而暂停程序其它部分的执行,这种情况下,就可以考虑创建一个线程,令它与那个事件或资源关联到一起,并让它独立于主程序运行。通过使用线程,可以避免用户在运行程序和得到结果之间的停顿,还可以让一些任务(如打印任务)在后台运行,而用户则在前台继续完成一些其它的工作。总之,利用多线程技术,可以使编程人员方便地开发出能同时处理多个任务的功能强大的应用程序。

二 、多线程的实现


  在传统的程序语言里,运行的顺序总是必须顺着程序的流程来走,遇到if-else语句就加以判断,遇到for、while等循环就会多绕几个圈,最后程序还是按着一定的程序走,且一次只能运行一个程序块。

Java的“多线程”打破了这种传统的束缚。所谓的线程(Thread)是指程序的运行流程,“多线程”的机制则是指可以同时运行多个程序块,使程序运行的效率变得更高,也可克服传统程序语言所无法解决的问题。例如:有些包含循环的线程可能要使用比较长的一段时间来运算,此时便可让另一个线程来做其它的处理。

本节将用一个简单的程序来说明单一线程与多线程的不同。ThreadDemo是单一线程的范例,其程序代码编写方法与前几节的程序代码并没有什么不同。


范例:ThreadDemo.java
   public class ThreadDemo{
    	public static void main(String args[]){
    		new TestThread().run();
    		for(int i=0; i<10; i++){//main方法内的循环体
    			System.out.println("main 线程在运行");
    		}
    	}
    }

TestThread.java
 class TestThread{
    	public void run(){
    		for(int i=0; i<10; i++){// run方法内的循环体
    			System.out.println("TestThread 在运行");
    		}
    	}
    }

从本例中可看出,要想运行main方法中的循环,必须要等TestThread类中的run()方法执行完之后才可以运行,这便是单一线程的缺陷,在Java里,是否可以同时运行run()方法与main()方法的语句呢?答案是肯定的,其方法是——多线程。

那么,怎么创建多线程,并启动多线程呢? 

2.1 继承Thread类
  /**
     * 使用继承java.lang.Thread类的方式创建一个线程
     * 
     */
    public class MyThread extends Thread {
    
        /**
         * 重写(Override)run()方法 JVM会自动调用该方法
         */
        public void run() {
            System.out.println("I'm running!");
        }
    }

启动线程:

MyThread t = new MyThread();
    t.start();

注意:重写(override) run()方法在该线程被启动后后,JVM会自动调用run()方法来执行任务;不需要我们手动去调用run()方法。

2.2 实现 Runnable 接口

    /**
     * 通过实现Runnable接口创建一个线程
     */
    public class MyRunnable implements Runnable {
        public void run() {
             System.out.println("I'm running!");
        }
    }

启动线程:

  MyRunnable mr = new MyRunnable();
    Thread t = new Thread(mr);
    t.start();


3.2调用start()与调用run()的区别
start() native 是请求主线程启动新的线程
run() 是线程启动调用的第一个方法

三、 两种实现方式的比较

1. 使用Runnable接口
   优点:还可以从其他类继承;
   优点:保持程序风格的一致性。
   优点:各个线程间可以比较方便共享资源(继承Thread类也可以,只是稍微比较麻烦而已)。
   缺点:编程稍复杂
2. 直接继承Thread类
   优点:编写和操作简单,可以直接操纵线程  
   缺点:不能再从其他类继承;

四、线程的常用方法

5.1线程的名称
    thread.getName();//返回系统分配的线程名字
5.2线程的睡眠
    Thread.sleep(long millis);让线程至少休眠millis毫秒
当前线程睡眠/millis的时间(millis指定睡眠时间是其最小的不执行时间,因为sleep(millis)休眠到达后,无法保证会被JVM立即调度); 作用:保持对象锁,让出CPU,调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留一定的时间给其他线程执行的机会;

5.3线程的中断(并不会马上中断,而是当前线程的方法isInterrupted()返回值变成true)
    thread.interrupt()//请求中断线程
判断某个线程是否已被发送过中断请求,请使用Thread.currentThread().isInterrupted()方法(因为它判断线程中断标示,若果为true时,不会立刻清除中断标示位,即不会将中断标设置为false),如果使用Thread.interrupted()(判断线程中断标示,若果为true时 会将中断标示位清除,即重新设置为false);
下面是线程在循环中时的中断方式。

    while(!Thread.currentThread().isInterrupted() && 其他条件){
        //线程耗时操作
    }

注意:如果一个线程处于了阻塞状态(如线程调用了thread.sleep进入阻塞状态),则在线程在检查中断标示时如果发现中断标示为true,则会在 sleep 阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。

  
  public void run() {
        try {
            ...
            /*
             * 不管循环里是否调用过线程阻塞的方法如sleep,这里还是需要加上
             * !Thread.currentThread().isInterrupted()条件,虽然抛出异常后退出了循环,显
             * 得用阻塞的情况下是多余的,但如果调用了阻塞方法但没有阻塞时,这样会更安全、更及时。
             */
            while (!Thread.currentThread().isInterrupted()&& 其他条件) {
              
              Thread.sleep(100);
              
                //线程耗时操作 
            }
        } catch (InterruptedException e) {
            //线程在sleep期间被中断了,做一些中断后的相关操作...
        } finally {
            //线程结束前做一些清理工作
        }
    }

5.4线程的停止

    thread.stop();//停止线程

Java API 明确说明,thread.stop()是不安全的。它的不安全主要是针对于二点:释放该线程所持有的所有的锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放,那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。

正确停止线程的解决方法:

  class MyThread extends Thread {
    
    	private boolean exit = false;
    
      //外部调用该方法来停止线程,而不推荐调用 stop 方法停止线程。
    	public void exit() {
    		exit = true;// 表示为退出状态
    		Thread.currentThread().interrupt();// 同时中断线程,防止线程正在休眠中
    	}
    
    	@Override
    	public void run() {
    		try {
    			while (!exit && !Thread.currentThread().isInterrupted() && 其他条件) {
    
    				Thread.sleep(100);
    
    				// 线程耗时操作
    			}
    		} catch (InterruptedException e) {
    			// 线程在sleep期间被中断了,做一些中断后的相关操作...
    		} finally {
    			// 线程结束前做一些清理工作
    		}
    	}
    }

五、 线程的生命周期


线程可以分为4个状态:

1. New(新生),线程在建立后并不马上执行run方法中的代码,而是处于等待状态。
2. Runnable(可运行):为了方便分析,还可将其分为:Runnable与Running。当调用start方法后,线程开始执行run方法中的代码。线程进入运行状态。
3. blocked(被阻塞/休眠),当运行中的线程 调用了 sleep() 方法,线程进入休眠状态。
4. Dead(死亡)。当使用stop方法终止线程,或使用interrupt方法终止线程,异或者线程的run方法的执行完成后(线程会自动被销毁),则线程进入死亡状态。

注意:一个线程一个栈
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值