进程和线程

 什么是进程?

  • 进程是操作系统中运行的一个任务(一个应用程序运行在一个进程中)
  • 进程(process)是一块包含了某些资源的内存区域,操作系统利用进程把它的工作划分为一些功能单元。
  • 进程包含一个或多个线程。进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。
  • 线程只能归属于一个进程并且它只能访问该进程所拥有的资源,当操作系统创建一个进程后,该进程会自动申请一个主线程或者首要线程的线程。

什么是线程

  • 一个线程是进程的一个顺序执行流
  • 同类的多个线程共享一块内存空间和一组系统资源,线程本身有一个供程序执行时的堆栈,线程是切换负荷小,因此线程也被成为轻负荷经常。

进程和线程的区别

  • 一个进程至少拥有一个线程
  • 进程拥有独立的内存单元,而多个线程共享内存。
  • 每个独立线程有一个程序运行的入口,顺序执行序列和程序的出口。
  • 线程不能独立执行

为什么要用多线程?

生活中的多线程:

  • 单车道容易拥堵,多车道,路况畅通。
  • 车间中一条生产线生产缓慢,增加多条生产线,同时生产出更多的产品。

使用场合:

  • 程序中需要同时完成多个任务的情况,可以将每个任务定义为一个线程,使他们一同工作。
  • 有些单一线程可以完成,但是多线程可以更快,如下载文件等

多线程的缺点:

  1. 如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换.
  2. 更多的线程需要更多的内存空间
  3. 线程中止需要考虑对程序运行的影响.
  4. 通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生

并发原理

多个线程“同时”运行只是我们感官上的一种表现。事实上线程是并发运行的(时间片轮转进程调度算法),单个CPU的情况下任何一个时间内有且仅有一个进程占有CPU,如果有多个CPU,情况就不同了,如果进程数小于CPU数,则不同的进程可以分配给不同的CPU来运行,这样,各个进程就是真正同时运行的,这便是并行。但如果进程数大于CPU数,则仍然需要使用并发技术。OS将时间划分为很多时间片段(时间片),尽可能的均匀分配给每一个线程,获取时间片段的线程被CPU运行,而其他线程全部等,只要每个时间片段足够小,用户根本感觉不出来CPU是在轮流为多个进程服务,就好象所有的进程都在不间断地运行一样。所有并发技术不是绝对意义上的“同时发生”。

线程的状态

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。

1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

多线程的创建

Java 提供了三种创建线程的方法:

1、使用thread创建并启动线程。

public class testThread {

    public static void main(String[] args){
        Thread t1=new MyThread();
        Thread t2=new MyThread();
        t1.start();
        t2.start();
    }
}

/*运行TestThread类,控制台会输出两次0-4。*/
class MyThread extends Thread{
    public void run(){
        for(int i=0;i<5;i++){
            System.out.println(i);
        } 
    }
}

 2、实现Runnable接口

public class testThread implements Runnable{

	public void run(){
		for(int i=0;i<5;i++){
			System.out.println(i);
		}	
	}
	public static void main(String[] args){
		testThread t1=new testThread();
		Thread t=new Thread(t1);
		t.start();
	}
}

 3、内部类的方式实现

    public static void main(String[] args){

        Thread t=new Thread(){
            public void run(){
                for(int i=0;i<5;i++){
                    System.out.println(i);
                }    
            }
        };
        t.start();
    }

 总结:

1、Thread类是线程类,每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程,run是定义该线程要执行的逻辑。启动线程是调用线程的start()方法。start()方法会将当前线程纳入线程调度,当线程获取时间片后会自动开始执行run方法中的逻辑。

2、实现runnable的好处在于可以将线程与线程要执行的任务分类开减少耦合,同时java是单继承的,这样可以去实现其他父类或者接口。

3、当一个线程仅需要一个实例是,推荐使用匿名内部类的方式创建线程,简化编写代码的复杂度。

原文地址:https://www.cnblogs.com/magic101/p/7827969.html