`
nenty
  • 浏览: 112349 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论
阅读更多
多线程
线程:是指进程中的一个执行流程。
线程与进程的区别:每个进程都需要操作系统为其分配独立的内存地址空间,而同一进程中的所有线程在同一块地址空间中工作,这些线程可以共享同一块内存和系统资源。


如何创建一个线程?

创建线程有两种方式,如下:
1、 扩展java.lang.Thread类
2、 实现Runnable接口
Thread类代表线程类,它的两个最主要的方法是:
run()——包含线程运行时所执行的代码
Start()——用于启动线程

一个线程只能被启动一次。第二次启动时将会抛出java.lang.IllegalThreadExcetpion异常

线程间状态的转换(如图示)

新建状态:用new语句创建的线程对象处于新建状态,此时它和其它的java对象一样,仅仅在堆中被分配了内存
就绪状态:当一个线程创建了以后,其他的线程调用了它的start()方法,该线程就进入了就绪状态。处于这个状态的线程位于可运行池中,等待获得CPU的使用权
运行状态:处于这个状态的线程占用CPU,执行程序的代码
阻塞状态:当线程处于阻塞状态时,java虚拟机不会给线程分配CPU,直到线程重新进入就绪状态,它才有机会转到运行状态。
阻塞状态分为三种情况:
1、 位于对象等待池中的阻塞状态:当线程运行时,如果执行了某个对象的wait()方法,java虚拟机就回把线程放到这个对象的等待池中
2、 位于对象锁中的阻塞状态,当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他的线程占用,JVM就会把这个线程放到这个对象的琐池中。
3、 其它的阻塞状态:当前线程执行了sleep()方法,或者调用了其它线程的join()方法,或者发出了I/O请求时,就会进入这个状态中。

死亡状态:当线程退出了run()方法,就进入了死亡状态,该线程结束了生命周期。
           或者正常退出
           或者遇到异常退出
           Thread类的isAlive()方法判断一个线程是否活着,当线程处于死亡状态或者新建状态时,该方法返回false,在其余的状态下,该方法返回true.

线程调度
线程调度模型:分时调度模型和抢占式调度模型
JVM采用抢占式调度模型。
所谓的多线程的并发运行,其实是指宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务。
(线程的调度不是跨平台,它不仅取决于java虚拟机,它还依赖于操作系统)

如果希望明确地让一个线程给另外一个线程运行的机会,可以采取以下的办法之一
1、 调整各个线程的优先级
2、 让处于运行状态的线程调用Thread.sleep()方法
3、 让处于运行状态的线程调用Thread.yield()方法
4、 让处于运行状态的线程调用另一个线程的join()方法

调整各个线程的优先级
Thread类的setPriority(int)和getPriority()方法分别用来设置优先级和读取优先级。
如果希望程序能够移值到各个操作系统中,应该确保在设置线程的优先级时,只使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY这3个优先级。

线程睡眠:当线程在运行中执行了sleep()方法时,它就会放弃CPU,转到阻塞状态。
线程让步:当线程在运行中执行了Thread类的yield()静态方法时,如果此时具有相同优先级的其它线程处于就绪状态,那么yield()方法将把当前运行的线程放到运行池中并使另一个线程运行。如果没有相同优先级的可运行线程,则yield()方法什么也不做。
Sleep()方法和yield()方法都是Thread类的静态方法,都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程,两者的区别在于:
         1、sleep()方法会给其他线程运行的机会,而不考虑其他线程的优先级,因此会给较低线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。
2、当线程执行了sleep(long millis)方法后,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法后,将转到就绪状态。
          3、sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常
          4、sleep()方法比yield()方法具有更好的移植性

等待其它线程的结束:join()
          当前运行的线程可以调用另一个线程的 join()方法,当前运行的线程将转到阻塞状态,直到另一个线程运行结束,它才恢复运行。

定时器Timer:在JDK的java.util包中提供了一个实用类Timer, 它能够定时执行特定的任务。

线程的同步
原子操作:根据Java规范,对于基本类型的赋值或者返回值操作,是原子操作。但这里的基本数据类型不包括long和double, 因为JVM看到的基本存储单位是32位,而long 和double都要用64位来表示。所以无法在一个时钟周期内完成。

自增操作(++)不是原子操作,因为它涉及到一次读和一次写。

原子操作:由一组相关的操作完成,这些操作可能会操纵与其它的线程共享的资源,为了保证得到正确的运算结果,一个线程在执行原子操作其间,应该采取其他的措施使得其他的线程不能操纵共享资源。

同步代码块:为了保证每个线程能够正常执行原子操作,Java引入了同步机制,具体的做法是在代表原子操作的程序代码前加上synchronized标记,这样的代码被称为同步代码块。

同步锁:每个JAVA对象都有且只有一个同步锁,在任何时刻,最多只允许一个线程拥有这把锁。

当一个线程试图访问带有synchronized(this)标记的代码块时,必须获得 this关键字引用的对象的锁,在以下的两种情况下,本线程有着不同的命运。
1、 假如这个锁已经被其它的线程占用,JVM就会把这个线程放到本对象的锁池中。本线程进入阻塞状态。锁池中可能有很多的线程,等到其他的线程释放了锁,JVM就会从锁池中随机取出一个线程,使这个线程拥有锁,并且转到就绪状态。
2、 假如这个锁没有被其他线程占用,本线程会获得这把锁,开始执行同步代码块。
(一般情况下在执行同步代码块时不会释放同步锁,但也有特殊情况会释放对象锁
如在执行同步代码块时,遇到异常而导致线程终止,锁会被释放;在执行代码块时,执行了锁所属对象的wait()方法,这个线程会释放对象锁,进入对象的等待池中)

线程同步的特征:
1、 如果一个同步代码块和非同步代码块同时操作共享资源,仍然会造成对共享资源的竞争。因为当一个线程执行一个对象的同步代码块时,其他的线程仍然可以执行对象的非同步代码块。(所谓的线程之间保持同步,是指不同的线程在执行同一个对象的同步代码块时,因为要获得对象的同步锁而互相牵制)
2、 每个对象都有唯一的同步锁
3、 在静态方法前面可以使用synchronized修饰符。
4、 当一个线程开始执行同步代码块时,并不意味着必须以不间断的方式运行,进入同步代码块的线程可以执行Thread.sleep()或者执行Thread.yield()方法,此时它并不释放对象锁,只是把运行的机会让给其他的线程。
5、 Synchronized声明不会被继承,如果一个用synchronized修饰的方法被子类覆盖,那么子类中这个方法不在保持同步,除非用synchronized修饰。

线程安全的类:
1、 这个类的对象可以同时被多个线程安全的访问。
2、 每个线程都能正常的执行原子操作,得到正确的结果。
3、 在每个线程的原子操作都完成后,对象处于逻辑上合理的状态。

释放对象的锁:
1、 执行完同步代码块就会释放对象的锁
2、 在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放
3、 在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放对象锁,进入对象的等待池。

死锁
当一个线程等待由另一个线程持有的锁,而后者正在等待已被第一个线程持有的锁时,就会发生死锁。JVM不监测也不试图避免这种情况,因此保证不发生死锁就成了程序员的责任。

如何避免死锁
一个通用的经验法则是:当几个线程都要访问共享资源A、B、C 时,保证每个线程都按照同样的顺序去访问他们。

线程通信
Java.lang.Object类中提供了两个用于线程通信的方法
1、 wait():执行了该方法的线程释放对象的锁,JVM会把该线程放到对象的等待池中。该线程等待其它线程唤醒
2、 notify():执行该方法的线程唤醒在对象的等待池中等待的一个线程,JVM从对象的等待池中随机选择一个线程,把它转到对象的锁池中。
线程同步
作者 : buaawhl

我们可以在计算机上运行各种计算机软件程序。每一个运行的程序可能包括多个独立运行的线程(Thread)。
线程(Thread)是一份独立运行的程序,有自己专用的运行栈。线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
同步这个词是从英文synchronize(使同时发生)翻译过来的。我也不明白为什么要用这个很容易引起误解的词。既然大家都这么用,咱们也就只好这么将就。
线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

因此,关于线程同步,需要牢牢记住的第一点是:线程同步就是线程排队。同步就是排队。线程同步的目的就是避免线程“同步”执行。这可真是个无聊的绕口令。
关于线程同步,需要牢牢记住的第二点是 “共享”这两个字。只有共享资源的读写访问才需要同步。如果不是共享资源,那么就根本没有同步的必要。
关于线程同步,需要牢牢记住的第三点是,只有“变量”才需要同步访问。如果共享的资源是固定不变的,那么就相当于“常量”,线程同时读取常量也不需要同步。至少一个线程修改共享资源,这样的情况下,线程之间就需要同步。
关于线程同步,需要牢牢记住的第四点是:多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码;无论是否执行同一份代码,只要这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。

为了加深理解,下面举几个例子。
有两个采购员,他们的工作内容是相同的,都是遵循如下的步骤:
(1)到市场上去,寻找并购买有潜力的样品。
(2)回到公司,写报告。
这两个人的工作内容虽然一样,他们都需要购买样品,他们可能买到同样种类的样品,但是他们绝对不会购买到同一件样品,他们之间没有任何共享资源。所以,他们可以各自进行自己的工作,互不干扰。
这两个采购员就相当于两个线程;两个采购员遵循相同的工作步骤,相当于这两个线程执行同一段代码。

下面给这两个采购员增加一个工作步骤。采购员需要根据公司的“布告栏”上面公布的信息,安排自己的工作计划。
这两个采购员有可能同时走到布告栏的前面,同时观看布告栏上的信息。这一点问题都没有。因为布告栏是只读的,这两个采购员谁都不会去修改布告栏上写的信息。

下面增加一个角色。一个办公室行政人员这个时候,也走到了布告栏前面,准备修改布告栏上的信息。
如果行政人员先到达布告栏,并且正在修改布告栏的内容。两个采购员这个时候,恰好也到了。这两个采购员就必须等待行政人员完成修改之后,才能观看修改后的信息。
如果行政人员到达的时候,两个采购员已经在观看布告栏了。那么行政人员需要等待两个采购员把当前信息记录下来之后,才能够写上新的信息。
上述这两种情况,行政人员和采购员对布告栏的访问就需要进行同步。因为其中一个线程(行政人员)修改了共享资源(布告栏)。而且我们可以看到,行政人员的工作流程和采购员的工作流程(执行代码)完全不同,但是由于他们访问了同一份可变共享资源(布告栏),所以他们之间需要同步。

同步锁

前面讲了为什么要线程同步,下面我们就来看如何才能线程同步。
线程同步的基本实现思路还是比较容易理解的。我们可以给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源。
生活中,我们也可能会遇到这样的例子。一些超市的外面提供了一些自动储物箱。每个储物箱都有一把锁,一把钥匙。人们可以使用那些带有钥匙的储物箱,把东西放到储物箱里面,把储物箱锁上,然后把钥匙拿走。这样,该储物箱就被锁住了,其他人不能再访问这个储物箱。(当然,真实的储物箱钥匙是可以被人拿走复制的,所以不要把贵重物品放在超市的储物箱里面。于是很多超市都采用了电子密码锁。)
线程同步锁这个模型看起来很直观。但是,还有一个严峻的问题没有解决,这个同步锁应该加在哪里?
当然是加在共享资源上了。反应快的读者一定会抢先回答。
没错,如果可能,我们当然尽量把同步锁加在共享资源上。一些比较完善的共享资源,比如,文件系统,数据库系统等,自身都提供了比较完善的同步锁机制。我们不用另外给这些资源加锁,这些资源自己就有锁。
但是,大部分情况下,我们在代码中访问的共享资源都是比较简单的共享对象。这些对象里面没有地方让我们加锁。
读者可能会提出建议:为什么不在每一个对象内部都增加一个新的区域,专门用来加锁呢?这种设计理论上当然也是可行的。问题在于,线程同步的情况并不是很普遍。如果因为这小概率事件,在所有对象内部都开辟一块锁空间,将会带来极大的空间浪费。得不偿失。
于是,现代的编程语言的设计思路都是把同步锁加在代码段上。确切的说,是把同步锁加在“访问共享资源的代码段”上。这一点一定要记住,同步锁是加在代码段上的。
同步锁加在代码段上,就很好地解决了上述的空间浪费问题。但是却增加了模型的复杂度,也增加了我们的理解难度。
现在我们就来仔细分析“同步锁加在代码段上”的线程同步模型。
首先,我们已经解决了同步锁加在哪里的问题。我们已经确定,同步锁不是加在共享资源上,而是加在访问共享资源的代码段上。
其次,我们要解决的问题是,我们应该在代码段上加什么样的锁。这个问题是重点中的重点。这是我们尤其要注意的问题:访问同一份共享资源的不同代码段,应该加上同一个同步锁;如果加的是不同的同步锁,那么根本就起不到同步的作用,没有任何意义。
这就是说,同步锁本身也一定是多个线程之间的共享对象。

分享到:
评论

相关推荐

    线程 JAVA java线程 java线程第3版 java线程第2版第3版合集

    电子书相关:包含4个有关JAVA线程的电子书(几乎涵盖全部有关线程的书籍) OReilly.Java.Threads.3rd.Edition.Sep.2004.eBook-DDU Java Thread Programming (Sams) java线程第二版中英文 java线程第二版中英文 ...

    java 线程工具类 java 线程工具类

    java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具...

    java线程.pdf

    根据提供的信息,我们可以推断出这份文档主要关注的是Java线程的相关内容。下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java...

    Java线程状态流转图

    Java线程状态流转图知识点总结 Java线程状态流转图是一种用于描述Java线程生命周期中不同的状态和状态转换的图形表示方式。该图形展示了Java线程从创建到终止的整个生命周期,并详细介绍了每种状态的特点和转换...

    Java线程详解大全

    Java线程是并发编程的核心部分,它允许程序在同一时间执行多个独立的任务,从而提高系统效率和响应速度。本文将深入探讨Java线程的概念、生命周期、实现方式以及相关的同步机制。 首先,理解线程的基本概念至关重要...

    java 线程 dump 分析工具 2.3.3

    java 线程Dump 分析工具: Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的...

    java线程分析工具TDA

    Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...

    java多线程Demo

    Java线程有10个优先级(MIN_PRIORITY, NORM_PRIORITY, MAX_PRIORITY),默认优先级是NORM_PRIORITY。但是,线程优先级并不保证绝对的执行顺序,操作系统调度策略可能影响实际执行顺序。 7. join()方法: 一个线程...

    Java线程.ppt

    Java线程是Java编程中的重要概念,特别是在多核处理器和并发处理中不可或缺。Java线程允许程序在同一时间执行多个不同的任务,从而提高了程序的效率和响应性。在燕山大学信息学院计算机系的课程中,李峰教授讲解了...

    Java线程(第三版)

    《Java线程(第三版)》是一本深入探讨Java线程技术的专业书籍,旨在帮助开发者理解和掌握Java平台上的多线程编程。Java线程是并发编程的重要组成部分,它允许程序同时执行多个任务,从而充分利用系统资源,提高程序的...

    java线程深入解析

    Java线程是Java编程语言中的核心概念,尤其在多任务处理和并发编程中扮演着重要角色。线程允许一个程序内部同时执行多个独立的控制流,使得程序能够更高效地利用处理器资源。本文将深入解析Java线程的相关知识点,...

    Java线程使用教程

    Java线程是Java编程语言中的一个核心概念,它允许程序同时执行多个任务,极大地提高了程序的并发性和效率。本教程将深入探讨Java线程的使用,帮助开发者掌握这一关键技术。 一、线程基础 1. **线程的概念**:线程...

    java线程实例 各种小Demo

    Java线程是多任务编程的重要概念,它允许程序同时执行多个独立的任务,从而提高系统效率和响应速度。在Java中,线程可以分为用户线程和守护线程,前者是程序运行的基础,而后者是在所有用户线程结束时才终止的后台...

    JAVA线程dump的分析

    JAVA线程dump的分析 JAVA线程dump是指在JAVA程序中,当前线程的状态和调用堆栈的快照,能够帮助开发者了解当前程序的执行情况,诊断问题和性能瓶颈。生成JAVA线程dump的方法在不同的操作系统下是不同的,在Windows...

    JAVA单线程多线程

    ### JAVA中的单线程与多线程概念解析 #### 单线程的理解 在Java编程环境中,单线程指的是程序执行过程中只有一个线程在运行。这意味着任何时刻只能执行一个任务,上一个任务完成后才会进行下一个任务。单线程模型...

    java线程文档大全

    Java线程是多任务编程中的核心概念,它允许程序同时执行多个不同的任务,极大地提高了程序的效率和响应性。在Java中,线程是通过Java.lang.Thread类或实现Runnable接口来创建和管理的。这份“java线程文档大全”包含...

    java线程入门级书籍

    ### Java线程入门知识点详解 #### 一、Java线程基础知识概述 **1.1 什么是线程?** 线程是程序执行流的最小单元,是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。在Java中...

    java线程.rar

    Java线程是Java编程语言中的一个核心概念,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,线程是通过类`Thread`或实现`Runnable`接口来创建和管理的。Java线程模型是基于操作系统的原生...

    JAVA线程学习(源代码)

    本资源"JAVA线程学习(源代码)"提供了关于Java线程的源代码示例,帮助我们深入理解和实践线程的使用。 首先,我们要理解Java中的线程模型。Java线程由`java.lang.Thread`类或`java.util.concurrent.Executor`框架来...

    关于线程(java)两天的课件

    Java线程有五种基本状态:新建(New)、可运行(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Terminated)。这些状态之间的转换是通过调用特定的方法,如start()、sleep()、join()、wait()、notify()等...

Global site tag (gtag.js) - Google Analytics