Java多线程-基础篇-1.什么是多线程

目录

基本概念

进程与线程

进程

线程

区别

并行与并发

线程状态

Thread.State

状态转换

线程的创建

继承Thread类

实现Runnable接口

实现Callable接口

[参考文献]


 

基本概念

  • 进程与线程

  • 进程

    进程是指具有某些独立功能的程序从加载、执行到结束的一次过程,是程序的一次执行。程序运行时系统会创建一个进程,并为它分配资源(CPU、内存等),然后将该进程交给进程就绪队列,直到进程调度器选中它并为它分配CPU时间,程序开始真正执行。进程是资源分配的最小单位。

    进程是一个动态的概念,动态创建、执行到动态消亡。进程创建后,有自己的地址空间,包括方法区、数据区以及堆栈区。

    进程的执行过程中可能具有三种基本形态:就绪状态、执行状态以及阻塞状态。如图所示

  • 线程

    线程是程序执行的最小单位,是系统独立调度和分派的基本单位,有时也被称作轻量级的进程(Lightweight,LWP)。一个进程可以由多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。

    多线程是指在单个进程中同时运行多个线程完成不同的工作。

  • 区别

    下面节选自《Unix网络编程》的一段描述:

《Unix网络编程》

fork is expensive. Memory is copied from the parent to the child, all descriptors are duplicated in the child, and so on. Current implementations use a technique called copy-on-write, which avoids a copy of the parent’s data space to the child until the child needs its own copy. But, regardless of this optimization, fork is expensive.

IPC is required to pass information between the parent and child after the fork. Passing information from the parent to the child before the fork is easy, since the child starts with a copy of the parent’s data space and with a copy of all the parent’s descriptors. But, returning information from the child to the parent takes more work.

Threads help with both problems. Threads are sometimes called lightweight processes since a thread is “lighter weight” than a process. That is, thread creation can be 10–100 times faster than process creation.

All threads within a process share the same global memory. This makes the sharing of information easy between the threads, but along with this simplicity comes the problem

     进程与线程之间的区别体现在以下几点:

  1. 进程系统资源分配的最小单位,所有启动进程后,要分配一个独立的空间,包括堆栈空间和数据段,系统开销很大;线程是程序执行的最小单位,只拥有独立运行需要的堆栈空间以及少量的数据段,大部分数据是线程间共享的;因此线程之间的切换速度效率远远高于进程。
  2. 通讯机制上,进程之间是相互独立,互不影响的,因此进程之间的通讯只能通过IPC;而进程中的线程之间是共享数据的,通讯容易实现,不过同样也带来了数据维护的复杂性等问题。
  3. 进程之间的相互独立性使得多进程程序更加健壮,一个进程的产生和消亡不会影响其他的进程;而多线程中只要有一个线程挂掉,整个进程也挂掉了。
  4. 一个线程必定属于一个进程,一个进程可以拥有多个线程并且至少拥有一个线程。
  • 并行与并发

    并行是指两个或者多个事件在不同的处理机上同一时刻发生;并发是指两个或者多个事件在同一个处理机上在同一时间间隔发生。

    举个简单的例子来描述这个区别。

    你在某宝经营着一个网店,生意特别火,一直有顾客下单以及咨询。你一个人不停地切换客户给他们处理订单或者回复他们的咨询,在顾客看来你是同一时间间隔内回复的(稍微有些延迟),这是并发的情景。后来你招聘客服一起同时答复顾客,这是并行的情景。

   举个简单的图片来描述下这个区别。

线程状态

Thread.State

线程Thread的状态定义在Thread.State这个枚举中,查看State可以看到Thread的状态可以分为

  1. New:线程的初始状态,也就是Thread thread = new Thread();刚创建完成时的状态;
  2. RUNNABLE:RUNNABLE是线程类被创建完成并调用start方法后的状态,细分的话可以分为可运行状态runnable和正在运行状态running。所谓可运行状态是线程类启动后尚未分配到CPU的调度时间,一直在"等待CPU"状态;
  3. BLOCKED:阻塞状态,当线程因为拿不到对象锁(synchronized)而处于等待的状态;
  4. WAITING:等待状态,等待其他进程的唤醒。造成的原因是调用了Object.wait()、join()或者LockSupport.park()方法。
  5. TIMED_WAITING:超时等待状态,是设定了超时时间的等待状态。造成的原因是调用了Thread.sleep(long)、Object.wait(long)、Object.wait(long)、join(long)、LockSupport.parkNanos(obj,long)和LockSupport.parkUtil(obj,long).
  6. TERMINATED:终止状态

状态转换

    线程状态的转换借用大神的图片来说明下~~

特殊解释下BLOCKED状态WAITING/TIMED_WAITING状态的区别。

线程Thread调用对象锁的wait()方法后,将会被置为WAITING/TIMED_WAITING状态,等待其他线程的Notify()/NotifyAll()。线程收到唤醒通知后,就会试着重新获取对象锁。不过同时可能会有很多个进程试图获取对象锁,这个时候只能有一个线程拿到对象锁,其他的线程将会置为BLOCKED状态。这就是BLOCKED状态WAITING/TIMED_WAITING状态的区别。至于线程之间的状态切换可以看 《Java多线程-基础篇-2.线程状态切换

线程的创建

线程的创建方式归结为以下几种:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

继承Thread类

继承Thread类提供了通过继承方式实现线程创建的方式,Thread类在底层是通过实现Runnable接口实现的。继承Thread类的子类需要重写run方法来实现自己的逻辑,线程启动时创建子类对象并调用start()方法。

    class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
    // 创建子类对象并调用start()方法
    PrimeThread p = new PrimeThread(143); 
    p.start();

实现Runnable接口

Java单继承的特性使得通过继承Thread类方式实现线程的创建具有更多的局限性。实现Runnable接口,实现run()方法。

     class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
    //同继承Thread类
    PrimeRun p = new PrimeRun(143);
    new Thread(p).start();

实现Callable接口

前两种方式是JDK5之前的经典模式,JDK5提供了Future模式的多线程编程。至于多线程的编程模式请参考《Java多线程-进阶篇-1.多线程编程模式》。Callable接口提供了一种带返回值并可以抛出异常的线程创建接口。

package com.lreis.learing.thread.create;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class ThreadCreate_Callable {

	public static void main(String[] args) {
		ExecutorService es = Executors.newCachedThreadPool();
		Future<RESULT_STATE> future = es.submit(new Callable<RESULT_STATE>() {
			public RESULT_STATE call() {
				try {
					// 线程阻塞100毫秒
					TimeUnit.MILLISECONDS.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				return RESULT_STATE.state_ok;
			}
		});
		System.out.println("任务完成了吗?" + future.isDone());
		try {
			//future.get()方法查询是阻塞线程的
			System.out.println(future.get().toString());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		es.shutdown();
		try {
			es.awaitTermination(1, TimeUnit.DAYS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	enum RESULT_STATE {
		// ok
		state_ok(0),
		// error
		state_error(1);
		private RESULT_STATE(int state) {
		}
	}
}

 

[参考文献]

1.Java中的多线程你只要看这一篇就够了

2.Java并发10:线程的状态Thread.State及其线程状态之间的转换

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值