Java多线程总结(一)多线程基础

本文转载地址:

            http://www.cnblogs.com/zrtqsk/p/3776328.html



  多线程是Java学习的非常重要的方面,是每个Java程序员必须掌握的基本技能。本文只是多线程细节、本质的总结,并无代码例子入门,不适合初学者理解。初学者学习多线程,建议一边看书、看博文,以便写代码尝试。

一、进程

  进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动。操作系统中,几乎所有运行中的任务对应一条进程(Process)。一个程序进入内存运行,即变成一个进程。进程是处于运行过程中的程序,并且具有一定独立功能。描述进程的有一句话非常经典——进程是系统进行资源分配和调度的一个独立单位。

  进程是系统中独立存在的实体,拥有自己独立的资源,拥有自己私有的地址空间进程的实质,就是程序在多道程序系统中的一次执行过程,它是动态产生,动态消亡的,具有自己的生命周期和各种不同的状态。进程具有并发性,它可以同其他进程一起并发执行,按各自独立的、不可预知的速度向前推进。 

(注意,并发性(concurrency)和并行性(parallel)是不同的。并行指的是同一时刻,多个指令在多台处理器上同时运行。并发指的是同一时刻只能有一条指令执行,但多个进程指令被被快速轮换执行,看起来就好像多个指令同时执行一样。)

  进程由程序数据进程控制块三部分组成。

二、线程

  线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

  线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

   在Java Web中要注意,线程是JVM级别的,在不停止的情况下,跟JVM共同消亡,就是说如果一个Web服务启动了多个Web应用,某个Web应用启动了某个线 程,如果关闭这个Web应用,线程并不会关闭,因为JVM还在运行,所以别忘了设置Web应用关闭时停止线程。

三、线程状态  

  

(图片出处:http://www.cnblogs.com/skywang12345/p/3479024.html)

线程共包括以下5种状态。
1. 新建状态(New)         : 线程对象被创建后,就进入了新建状态。此时它和其他Java对象一样,仅仅由Java虚拟机分配了内存,并初始化其成员变量值。

2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被调用了该对象的start()方法,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器。处于就绪状态的线程,随时可能被CPU调度执行,取决于JVM中线程调度器的调度。

3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。

4. 阻塞状态(Blocked)  : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
    (02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
    (03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5. 死亡状态(Dead)    : 线程执行完了、因异常退出了run()方法或者直接调用该线程的stop()方法(容易导致死锁,现在已经不推荐使用),该线程结束生命周期。

四、wait()、notify()、nofityAll()方法 

  在Object.java中,定义了wait(), notify()和notifyAll()等方法。

  wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁

  而 notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。

Object类中关于等待/唤醒的API详细信息如下:
  notify()        -- 唤醒在此对象监视器上等待的单个线程,使其进入“就绪状态”。  
  notifyAll()   -- 唤醒在此对象监视器上等待的所有线程,使其进入“就绪状态”。
  wait()                                     -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
  wait(long timeout)                 -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
  wait(long timeout, int nanos) -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。

wait()的作用是让“当前线程”等待(会释放锁),而“当前线程”是指正在cpu上运行的线程!

此处,http://www.cnblogs.com/skywang12345/p/3479224.html例子讲的非常详细。

五、yield()、sleep()、join()和interrupt()方法  

1、yield() 

  yield()是Thread类的静态方法。它能让当前线程暂停,但不会阻塞该线程,而是由“运行状态”进入到“就绪状态”,从而让 其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是 当前线程又进入到“运行状态”继续运行!

  值得注意的是,yield()方法不会释放锁

2、sleep()

  sleep()是Thread类的静态方法。该方法声明抛出了InterrupedException异常。所以使用时,要么捕捉,要么声明抛出。

  有2种重载方式:

——static void sleep(long millis)  :  让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准度的影响。

——static void sleep(long millis , int nanos)  :  让当前正在执行的线程暂停millis毫秒加nanos微秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准度的影响。

  sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。常用来暂停程序的运行。  

  同时注意,sleep()方法不会释放锁

3、join()

  join() 是Thread的一个实例方法。表示,当某个程序执行流中调用其他线程的join方法时,调用线程将被阻塞,直到被join的线程执行完毕。

有3种重载的形式:

——join()  :  等待被join的线程执行完成

——join(long millis)  :  等待被join的线程的时间最长为millis毫秒,若在millis毫秒内,被join的线程还未执行结束,则不等待。

——join(long millis , int nanos)  :  等待被join的线程的时间最长为millis毫秒加nanos微秒,若在此时间内,被join的线程还未执行结束,则不等待。

即当前线程内,用某个线程对象调用join()后,会使当前线程等待,直到该线程对象的线程运行完毕,原线程才会继续运行。

4、interrupt()   

  我们经常通过判断线程的中断标记来控制线程。   

  interrupt()是Thread类的一个实例方法,用于中断本线程。这个方法被调用时,会立即将线程的中断标志设置为“true”。所以当中断处于“阻塞状态”的线程时,由于处于阻塞状态,中断标记会被设置为“false”,抛出一个 InterruptedException。所以我们在线程的循环外捕获这个异常,就可以退出线程了。

  interrupt()并不会中断处于“运行状态”的线程,它会把线程的“中断标记”设置为true,所以我们可以不断通过isInterrupted()来检测中断标记,从而在调用了interrupt()后终止线程,这也是通常我们对interrupt()的用法。

  Interrupted()是Thread类的一个静态方法,它返回一个布尔类型指明当前线程是否已经被中断,isInterrupted()是Thread类的实例方法,返回一个布尔类型来判断线程是否已经被中断。它们都能够用于检测对象的“中断标记”。区别是,interrupted()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。

六、 Synchronized关键字

1、原理

  在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。

  当当前线程调用某对象的synchronized方法时,就获取了该对象的同步锁。例如,synchronized(obj),当前线程就获取了“obj这个对象”的同步锁。

  不同线程对同步锁的访问是互斥的。也就是说,某时间点,对象的同步锁只能被一个线程获取到!通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。
例如,现在有个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁” ——
线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。

2、基本规则

  第一条 :  当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
  第二条 :  当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块
  第三条 :  当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

3、实例锁和全局锁

实例锁 -- 锁在某一个实例对象上。如果该类是单例,那么该锁也具有全局锁的概念。
               实例锁对应的就是synchronized关键字。
全局锁 -- 该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。
               全局锁对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。

  就是说,一个非静态方法上的synchronized关键字,代表该方法依赖其所属对象。一个静态方法上synchronized关键字,代表该方法依赖这个类本身。

七、线程优先级和守护线程 

1、线程优先级  

  java中的线程优先级的范围是1~10,默认的优先级是5。每个线程默认的优先级都与创建它的父线程具有相同的优先级。默认情况下,mian线程具有普通优先级。“高优先级线程”会优先于“低优先级线程”执行。Thread提供了setPriority(int newPriority)和getPriority()方法来设置和返回线程优先级。

  Thread类有3个静态常量:

——MAX_PRIORITY = 10

——MIN_PRIORITY = 1

——NORM_PRIORITY = 5

2、守护线程

  java 中有两种线程:用户线程守护线程。可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。
用户线程一般用户执行用户级任务,而守护线程也就是“后台线程”,一般用来执行后台任务。需要注意的是:Java虚拟机在“用户线程”都结束后会后退出。

  守护线程又称“后台线程”、“精灵线程”,它有一个特征——如果所有前台线程都死亡,后台线程自动死亡

  通过setDaemon(true)来设置一个线程。

关于Java各种知识的总结,推荐大家一位博主skywang12345, 他的各种Java知识总结实在是详细易懂且经典,给了我不少帮助。

参考:http://www.cnblogs.com/skywang12345/p/java_threads_category.html

  《疯狂Java讲义》

时间: 2024-07-30 16:13:23

Java多线程总结(一)多线程基础的相关文章

java核心-多线程(4)-线程类基础知识

1.并发 <1>使用并发的一个重要原因是提高执行效率.由于I/O等情况阻塞,单个任务并不能充分利用CPU时间.所以在单处理器的机器上也应该使用并发. <2>为了实现并发,操作系统层面提供了.但是进程的数量和开销都有限制,并且多个进程之间的数据共享比较麻烦.另一种比较轻量的并发实现是使用线程,一个进程可以包含多个线程.线程在进程中没有数量限制, 数据共享相对简单.线程的支持跟语言是有关系的.Java 语言中支持多线程. <3>Java 中的多线程是抢占式的.这意味着一个任

Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过start方法启动线程--->线程变为可运行可执行状态,然后通过数据产生共享,线程产生互斥---->线程状态变为阻塞状态---->阻塞状态想打开的话可以调用notify方法. 这里Java5中提供了封装好的类,可以直接调用然后构造阻塞状态,以保证数据的原子性. 2.如何实现? 主要是实现Blo

Java多线程概念简介 多线程中篇(一)

Java的线程与操作系统的线程 在线程的相关介绍中,有讲到“线程的实现”分为三种:内核支持,用户级以及两者混合.(这只是一种简要的分类) Java线程在JDK1.2之前,是用户线程实现的 而在JDK1.2中,线程模型变为基于操作系统原生线程模型来实现的 所以说Java虚拟机中线程的映射实现,是受制于操作系统的,操作系统支持怎样的线程模型,决定了Java虚拟机中线程的样子. 虚拟机规范中也并未限定Java线程需要使用哪种线程模型来实现. 线程模型只对线程的并发规模和操作成本产生影响,对Java程序

Java之旅--关于多线程

关于多线程的知识,有非常多的资料可以参考.这里稍微总结一下,以求加深记忆. 关于多线程在日常工作中的使用:对于大多数的日常应用系统,比如各种管理系统,可能根本不需要深入了解,仅仅知道Thread/Runnable就够了:如果是需要很多计算任务的系统,比如推荐系统中各种中间数据的计算,对多线程的使用就较为频繁,也需要进行一下稍微深入的研究. ThreadLocal与synchronized 区别ThreadLocal 与 synchronized ThreadLocal是一个线程隔离(或者说是线程

Java核心知识点学习----多线程 倒计时记数器CountDownLatch和数据交换的Exchanger

本文将要介绍的内容都是Java5中的新特性,一个是倒计时记数器---CountDownLatch,另一个是用于线程间数据交换的Exchanger. 一.CountDownLatch 1.什么是CountDownLatch? 倒计时计数器,调用CountDownLatch对象的CountDown()方法就将计数器减一,当计数到达0时,则所有等待者或者全部等待者开始执行. 2.如何用? new CountDownLatch(1); 直接new,其构造函数必须传一个int类型的参数,参数的意思是: c

Java学习手记2——多线程

一.线程的概念 CPU执行程序,就好比一个人在干事情一样,同一个时间你只能做一件事情,但是这样的效率实在是太低了,在你用电脑的时候,听歌就不能浏览网页,看电影就不能下载视频,你想想是不是很蛋疼. 所以为了解决这个问题,CPU设计成了分时处理的方式,即不同的时间CPU做不同的事情,这样就大大提高了效率,能这样做的原因是因为CPU相对于人的接收信息的速度,快得多!也就是说,你边听歌边浏览网页,看上去好像音乐播放器和浏览器两个程序在同时工作,其实他们是分时在工作,即播放器工作一会,浏览器工作一会,这样

Java 并发和多线程(一) Java并发性和多线程介绍[转]

作者:Jakob Jenkov 译者:Simon-SZ  校对:方腾飞 http://tutorials.jenkov.com/java-concurrency/index.html 在过去单CPU时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一定的时间片运行. 随着多任务对软件开发者带来的

Java简明教程 12.多线程(multithreading)

单线程和多线程 关于它们的区别,zhihu上有一个回答,我认为十分不错,如下: 1. 单进程单线程:一个人在一个桌子上吃菜. 2. 单进程多线程:多个人在同一个桌子上一起吃菜. 3. 多进程单线程:多个人每个人在自己的桌子上吃菜. 多线程的问题是多个人同时吃一道菜的时候容易发生争抢.例如两个人同时夹一个菜,一个人刚伸出筷子,结果伸到的时候已经被夹走菜了.此时就必须等一个人夹一口之后,在还给另外一个人夹菜,也就是说资源共享就会发生冲突争抢. 例子: 多线程: 浏览器浏览一个页面,里面有很多图片,多

Java笔记(23):多线程(01)

1.多进程及多线程的意义:多进程:单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情.举例:一边玩游戏(游戏进程),一边听音乐(音乐进程).也就是说现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务.并且呢,可以提高CPU的使用率.多线程:多线程的存在,不是提高程序的执行速度.其实是为了提高应用程序的使用率.程序的执行其实都是在抢CPU的资源,CPU的执行权.多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权.我们是不敢保证哪

java swing实现的多线程实例代码教程-赛马demo

代码下载:http://www.zuidaima.com/share/1825492473826304.htm 原文:java swing实现的多线程实例代码教程-赛马demo 项目截图: 运行截图: java swing实现的多线程实例代码教程-赛马demo,布布扣,bubuko.com