疯狂Java学习笔记(62)------------线程初识

线程初识

没有线程编程的程序好比一个人一只手干活,而多线程的程序就好比一个人多只手干活.

进程是系统资源分配的独立单位,而线程是可调度运行的独立单位,一个进程可以拥有多个线程,线程是进程并行完成的多个任务.

进程、线程、并发执行

首先我们先来认识一下进程、线程、并发执行的概念:

  一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进程。启动进程的时候,操作系统会为进程分配资源,其中最主要的资源是内存空间,因为程序是在内存中运行的。

在进程中,有些程序流程块是可以乱序执行的,并且这个代码块可以同时被多次执行。实际上,这样的代码块就是线程体。线程是进程中乱序执行的代码流程。当多个线程同时运行的时候,这样的执行模式成为并发执行。

下面我以一个日常生活中简单的例子来说明进程和线程之间的区别和联系:

这副图是一个双向多车道的道路图,假如我们把整条道路看成是一个“进程”的话,那么图中由白色虚线分隔开来的各个车道就是进程中的各个“线程”了。

  1. 这些线程(车道)共享了进程(道路)的公共资源(土地资源)。
  2. 这些线程(车道)必须依赖于进程(道路),也就是说,线程不能脱离于进程而存在(就像离开了道路,车道也就没有意义了)。
  3. 这些线程(车道)之间可以并发执行(各个车道你走你的,我走我的),也可以互相同步(某些车道在交通灯亮时禁止继续前行或转弯,必须等待其它车道的车辆通行完毕)。
  4. 这些线程(车道)之间依靠代码逻辑(交通灯)来控制运行,一旦代码逻辑控制有误(死锁,多个线程同时竞争唯一资源),那么线程将陷入混乱,无序之中。
  5. 这些线程(车道)之间谁先运行是未知的,只有在线程刚好被分配到CPU时间片(交通灯变化)的那一刻才能知道。

并发和并行的区别就是一个处理器同时处理多个任务和多个处理器或者是多核的处理器同时处理多个不同的任务.

并发是逻辑上的同时发生(simultaneous),并行者是物理上的同时发生.

并发性 (concurrency),又称共行性,是指能处理多个同时性活动的能力,并发事件之间不一定要同一时刻发生.

并行性 (parallelism) 是指同时发生的两个并发事件,具有并发的含义,而并发则不一定并行.

也就是说,多进程之间并发执行,而多线程编程就是并行执行的了.

来个比喻:并发和并行的区别就是一个人同时吃三个馒头和三个人同时吃三个馒头.

这样,对于进程与线程,并发和并行的区别,您应该了解了吧.反正我是明白了!

Java中的线程

在Java中,“线程”指两件不同的事情:

1、java.lang.Thread类的一个实例;

2、线程的执行。

使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义、实例化和启动新线程。

一个Thread类实例只是一个对象,像Java中的任何其他对象一样,具有变量和方法,生死于堆上。

Java中,每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行着。

一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内,它被称为主线程。

一旦创建一个新的线程,就产生一个新的调用栈。

线程总体分两类:用户线程和守候线程。

当所有用户线程执行完毕的时候,JVM自动关闭。但是守候线程却不独立于JVM,守候线程一般是由操作系统或者用户自己创建的。(此处仍有疑问!)

JVM与多线程

Java编写的程序都运行在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。

每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。在这个JVM环境中,所有程序代码的运行都是以线程来运行的。JVM找到程序的入口点main(),然后运行main()方法,这样就产生了一个线程,这个线程称之为主线程。当main方法结束后,主线程运行完成。JVM进程也随即退出。

操作系统将进程线程进行管理,轮流(没有固定的顺序)分配每个进程很短的一段时间(不一定是均分),然后在每个进程内部,程序代码自己处理该进程内部线程的时间分配,多个线程之间相互的切换去执行,这个切换时间也是非常短的。

Java语言对多线程的支持

Java语言对多线程的支持通过类Thread和接口Runnable来实现。这里就不多说了。这里重点强调两个地方:

// 主线程其它代码段

ThreadClass subThread = new ThreadClass();

subThread.start();

// 主线程其它代码段

subThread.sleep(1000);

有人认为以下的代码在调用start()方法后,肯定是先启动子线程,然后主线程继续执行。在调用sleep()方法后CPU什么都不做,就在那里等待休眠的时间结束。实际上这种理解是错误的。因为:

  1. start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。
  2. Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会(也就是靠内部自己协调)。

线程的状态切换

前面我们提到,由于线程何时执行是未知的,只有在CPU为线程分配到时间片时,线程才能真正执行。在线程执行的过程中,由可能会因为各种各样的原因而暂停(就像前面所举的例子一样:汽车只有在交通灯变绿的时候才能够通行,而且在行驶的过程中可能会出现塞车,等待其它车辆通行或转弯的状况)。

这样线程就有了“状态”的概念,下面这副图很好的反映了线程在不同情况下的状态变化。

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

Java中线程的调度API

Java中关于线程调度的API最主要的有下面几个:

  1. 线程睡眠:Thread.sleep(long millis)方法
  2. 线程等待:Object类中的wait()方法
  3. 线程让步:Thread.yield() 方法
  4. 线程加入:join()方法
  5. 线程唤醒:Object类中的notify()方法

关于这几个方法的详细应用,可以参考SUN的API。这里我重点总结一下这几个方法的区别和使用。

sleep方法与wait方法的区别:

  1. sleep方法是静态方法,wait方法是非静态方法。
  2. sleep方法在时间到后会自己“醒来”,但wait不能,必须由其它线程通过notify(All)方法让它“醒来”。
  3. sleep方法通常用在不需要等待资源情况下的阻塞,像等待线程、数据库连接的情况一般用wait。

sleep/wait与yeld方法的区别:调用sleep或wait方法后,线程即进入block状态,而调用yeld方法后,线程进入runnable状态。

wait与join方法的区别:

  1. wait方法体现了线程之间的互斥关系,而join方法体现了线程之间的同步关系。
  2. wait方法必须由其它线程来解锁,而join方法不需要,只要被等待线程执行完毕,当前线程自动变为就绪。
  3. join方法的一个用途就是让子线程在完成业务逻辑执行之前,主线程一直等待直到所有子线程执行完毕。

通过上面的介绍相信同学们对java里面的多线程已经有了基本的了解和认识。其实多线程编程并没有大家想象中的那么难,只要在实际的学习,工作当中不断的加以练习和使用,相信大家很快就能掌握其中的奥妙,从而编写出赏心悦目的java程序。

本文借鉴资料:

http://express.ruanko.com/ruanko-express_6/webpage/tech4.html

http://lavasoft.blog.51cto.com/62575/27069/

时间: 2024-10-09 22:08:36

疯狂Java学习笔记(62)------------线程初识的相关文章

java学习笔记14--多线程编程基础1

本文地址:http://www.cnblogs.com/archimedes/p/java-study-note14.html,转载请注明源地址. 多线程编程基础 多进程 一个独立程序的每一次运行称为一个进程,例如:用字处理软件编辑文稿时,同时打开mp3播放程序听音乐,这两个独立的程序在同时运行,称为两个进程 进程要占用相当一部分处理器时间和内存资源 进程具有独立的内存空间 通信很不方便,编程模型比较复杂 多线程 一个程序中多段代码同时并发执行,称为多线程,线程比进程开销小,协作和数据交换容易

【疯狂Java学习笔记】【理解面向对象】

[学习笔记]1.Java语言是纯粹的面向对象语言,这体现在Java完全支持面向对象的三大基本特征:封装.继承.多态.抽象也是面向对象的重要组成部分,不过它不是面向对象的特征之一,因为所有的编程语言都需要抽象. 2.面向对象开发方法比较结构化开发方法的优势在于可以提供更好的可重用性.可扩展性.可维护性. 3.基于对象和面向对象的区别:基于对象也使用了对象,但是无法通过现有的对象作为模板来生成新的对象类型,继而产生新的对象,也就是说,基于对象没有继承的特点.而面向对象有继承,而多态则是建立在继承的基

疯狂java学习笔记之面向对象(八) - static和final

一.static: 1.static是一个标识符: - 有static修饰的成员表明该成员是属于类的; - 没有static修饰的成员表明该成员是属于实例/对象的. 2.static修饰的成员(Field.方法.初始化块),与类共存亡:static修饰的成员建议总是通过类名来访问,虽然它也可以通过实例来访问(实质也是通过类来访问的),所以平时若在其他程序中见到通过实例/对象来访问static成员时,可以直接将实例/对象 替换成类名: 3.程序都是先有类再有对象的,有可能出现有类但没有实例/对象的

疯狂Java学习笔记(63)-----------线程进阶

Java线程各个阶段的状态: 那么我们开始一点点研究吧: 启动线程 一.定义线程 1.扩展java.lang.Thread类. 此类中有个run()方法,应该注意其用法: public void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法:否则,该方法不执行任何操作并返回.   Thread 的子类应该重写该方法. 2.实现java.lang.Runnable接口. void run() 使用实现接口 Runnable

疯狂Java学习笔记(59)-----------50道Java线程面试题

50道Java线程面试题 下面是Java线程相关的热门面试题,你可以用它来好好准备面试. 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒.Java在语言层面对多线程提供了卓越的支持,它也是一个很好的卖点.欲了解更多详细信息请点击这里. 2) 线程和进程有什么区别? 线程是进程的子集,一个

疯狂Java学习笔记(64)------------线程转换

线程的生命周期 由上图可以看出: 线程的状态转换是线程控制的基础.线程状态总的可分为五大状态:分别是生.死.可运行.运行.等待/阻塞. 各个状态细节不再赘述,具体请看http://blog.csdn.net/u011225629/article/details/46288995此篇博文 此篇博文主要讲述各个状态的实例! 一.新建和就绪状态 此处不做细致的讲解,在上一篇博文中已经讲解过了! package com.haixu.thread2; public class InvokeRun exte

疯狂Java学习笔记(65)---------线程协作

线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,

疯狂Java学习笔记(51)-----------面试题

自己做了一点面试题,感觉很经典,分享给大家,发现还有很多东西需要学! 一.String,StringBuffer, StringBuilder 的区别是什么?String为什么是不可变的? 答:   1.String是字符串常量,StringBuffer和StringBuilder都是字符串变量.后两者的字符内容可变,而前者创建后内容不可变. 2.String不可变是因为在JDK中String类被声明为一个final类. 3.StringBuffer是线程安全的,而StringBuilder是非

疯狂Java学习笔记(89)-----------Java习惯用法总结

在Java编程中,有些知识 并不能仅通过语言规范或者标准API文档就能学到的.在本文中,我会尽量收集一些最常用的习惯用法,特别是很难猜到的用法.(Joshua Bloch的<Effective Java>对这个话题给出了更详尽的论述,可以从这本书里学习更多的用法.) 我把本文的所有代码都放在公共场所里.你可以根据自己的喜好去复制和修改任意的代码片段,不需要任何的凭证. 目录 实现: equals() hashCode() compareTo() clone() 应用: StringBuilde