java多线程基本概念与简单实用

概述

程序:Program,是一个静态的概念

进程:Process,是一个动态的概念

进程是程序的一次动态执行过程, 占用特定的地址空间。每个进程都是独立的,由3部分组成cpu,data,code

缺点:内存的浪费,cpu的负担

线程:Thread,是进程中一个“单一的连续控制流程”  (a single sequential flow ofcontrol)/执行路径。线程又被称为轻量级进程(lightweight process)。

一个进程可拥有多个并行的(concurrent)线程。一个进程中的线程共享相同的内存单元/内存地址空间,可以访问相同的变量和对象,而且它们从同一堆中分配对象、通信、数据交换、同步操作。由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。

实现

在Java中负责线程的这个功能的是Java.lang.Thread 这个类,可以通过创建 Thread 的实例来创建新的线程。每个线程都是通过某个特定Thread对象所对应的方法run( )来完成其操作的,方法run( )称为线程体。通过调用Thead类的start()方法来启动一个线程。

继承Thread类方式的缺点:那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread 类

通过Runnable接口实现多线程。优点:可以同时实现继承。实现Runnable接口方式要通用一些。

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5

Thread.MIN_PRIORITY = 1

Thread.MAX_PRIORITY = 10

Thread.NORM_PRIORITY = 5

使用下述线方法获得或设置线程对象的优先级。

intgetPriority();

voidsetPriority(int newPriority);

注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高后调用优先级低的线程。

Demo

public static void main(String[] args) {
	Thread thread1 = new Thread(new TestThread2(1));
	Thread thread2 = new Thread(new TestThread2(2));

	t1.setPriority(1);
	t2.setPriority(10);

	thread1.start();
	thread2.start();
}

public void run() {
	for(int i=0;i<100;i++){
		System.out.println(this.threadId+":"+i);
	}
}

状态

新生状态:

用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable)

就绪状态:

处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列,等待系统为其分配CPU。等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。

运行状态:

在运行状态的线程执行自己的run方法中代码,直到调用其他方法而终止、或等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。

阻塞状态:

处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。 在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。

死亡状态:

死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个。一个是正常运行的线程完成了它的全部工作;另一个是线程被强制性地终止,如通过执行stop或destroy方法来终止一个线程。不推荐使用这两个方法,前者会产生异常,后者是强制终止,不会释放锁。

同步与死锁

由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。

所谓死锁,是指多个进程循环等待它方占有的资源而无限期地僵持下去的局面。很显然,如果没有外力的作用,那么死锁涉及到的各个进程都将永远处于封锁状态。从上面的例子可以看出,计算机系统产生死锁的根本原因就是资源有限且操作不当。即:一种原因是系统提供的资源太少了,远不能满足并发进程对资源的需求。这种竞争资源引起的死锁是我们要讨论的核心。例如:消息是一种临时性资源。某一时刻,进程A等待进程B发来的消息,进程B等待进程C发来的消息,而进程C又等待进程A发来的消息。消息未到,A,B,C三个进程均无法向前推进,也会发生进程通信上的死锁。另一种原因是由于进程推进顺序不合适引发的死锁。资源少也未必一定产生死锁。就如同两个人过独木桥,如果两个人都要先过,在独木桥上僵持不肯后退,必然会应竞争资源产生死锁;但是,如果两个人上桥前先看一看有无对方的人在桥上,当无对方的人在桥上时自己才上桥,那麽问题就解决了。所以,如果程序设计得不合理,造成进程推进的顺序不当,也会出现死锁。

死锁的恢复

一旦在死锁检测时发现了死锁,就要消除死锁,使系统从死锁状态中恢复过来。

(1)最简单,最常用的方法就是进行系统的重新启动,不过这种方法代价很大,它意味着在这之前所有的进程已经完成的计算工作都将付之东流,包括参与死锁的那些进程,以及未参与死锁的进程。

(2)撤消进程,剥夺资源。终止参与死锁的进程,收回它们占有的资源,从而解除死锁。这时又分两种情况:一次性撤消参与死锁的全部进程,剥夺全部资源;或者逐步撤消参与死锁的进程,逐步收回死锁进程占有的资源。一般来说,选择逐步撤消的进程时要按照一定的原则进行,目的是撤消那些代价最小的进程,比如按进程的优先级确定进程的代价;考虑进程运行时的代价和与此进程相关的外部作业的代价等因素。

此外,还有进程回退策略,即让参与死锁的进程回退到没有发生死锁前某一点处,并由此点处继续执行,以求再次执行时不再发生死锁。虽然这是个较理想的办法,但是操作起来系统开销极大,要有堆栈这样的机构记录进程的每一步变化,以便今后的回退,有时这是无法做到的。

任务调度

Timer定时器类

TimerTask任务类

通过java timer  timetask:(spring的任务调度就是通过他们来实现的)

在这种实现方式中,Timer类实现的是类似闹钟的功能,也就是定时或者每隔一定时间触发一次线程。其实,Timer类本身实现的就是一个线程,只是这个线程是用来实现调用其它线程的。而TimerTask类是一个抽象类,该类实现了Runnable接口,所以按照前面的介绍,该类具备多线程的能力。

在这种实现方式中,通过继承TimerTask使该类获得多线程的能力,将需要多线程执行的代码书写在run方法内部,然后通过Timer类启动线程的执行。

在实际使用时,一个Timer可以启动任意多个TimerTask实现的线程,但是多个线程之间会存在阻塞。所以如果多个线程之间如果需要完全独立运行的话,最好还是一个Timer启动一个TimerTask实现。

业务思想

多线程机制操作,独立指令流并发执行,使得用户很轻松。

在此讲述了 Java 多线程编程的方方面面,包括创建线程,以及对多个线程进行调度、管理。深刻认识到了多线程编程的复杂性,以及线程切换开销带来的多线程程序的低效性,这也促使我们认真地思考一个问题:我们是否需要多线程?何时需要多线程?

多线程的核心在于多个代码块并发执行,本质特点在于各代码块之间的代码是乱序执行的。我们的程序是否需要多线程,就是要看这是否也是它的内在特点。

假如我们的程序根本不要求多个代码块并发执行,那自然不需要使用多线程;假如我们的程序虽然要求多个代码块并发执行,但是却不要求乱序,则我们完全可以用一个循环来简单高效地实现,也不需要使用多线程;只有当它完全符合多线程的特点时,多线程机制对线程间通信和线程管理的强大支持才能有用武之地,这时使用多线程才是值得的。

时间: 2024-10-14 18:45:27

java多线程基本概念与简单实用的相关文章

Java 多线程编程两个简单的例子

/** * @author gao */ package gao.org; public class RunnableDemo implements Runnable{ @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<10;i++){ System.out.println("新线程输出:"+i); } } public static void main(String []

Java 多线程编程两个简单的样例

/** * @author gao */ package gao.org; public class RunnableDemo implements Runnable{ @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<10;i++){ System.out.println("新线程输出:"+i); } } public static void main(String []

拨开云雾见天日 —— Java多线程编程概念剖析

说到Java多线程编程,大多数人都会想到继承Thread或实现Runnable编程,new 一个Thread实例,调用start()方法,由OS调用即可.具体过程如下: public class MyThread extends Thread {     @Override     public void run() {         System.out.println("MyThread");     }     public static void main(String[] 

Java多线程编程— 概念以及经常使用控制

多线程能满足程序猿编写很有效率的程序来达到充分利用CPU的目的,由于CPU的空暇时间可以保持在最低限度.有效利用多线程的关键是理解程序是并发运行而不是串行运行的.比如:程序中有两个子系统须要并发运行,这时候就须要利用多线程编程. 线程的运行中须要使用计算机的内存资源和CPU. 一.    进程与线程的概念 这两者的概念,这里仅仅给出自己狭隘的理解: 进程:进程是一个独立的活动的实体,是系统资源分配的基本单元. 它能够申请和拥有系统资源. 每一个进程都具有独立的代码和数据空间(进程上下文). 进程

Java多线程编程— 概念以及常用控制

多线程能满足程序员编写非常有效率的程序来达到充分利用CPU的目的,因为CPU的空闲时间能够保持在最低限度.有效利用多线程的关键是理解程序是并发执行而不是串行执行的.例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程.线程的运行中需要使用计算机的内存资源和CPU. 一.    进程与线程的概念 这两者的概念,这里只给出自己狭隘的理解: 进程:进程是一个独立的活动的实体,是系统资源分配的基本单元.它可以申请和拥有系统资源.每个进程都具有独立的代码和数据空间(进程上下文).进程的切换会有

Java多线程基本概念

基本概念 线程与任务的概念不一样. 任务:通常是一些抽象的且离散的工作单元,比如在Web请求中,针对用户的请求需要返回相应的页面是一个任务,在Java中实现Runnable接口的类也是一个任务. 线程:执行任务的实体,可以在单个线程中串行地执行各项任务,例如单线程串行执行Web请求,也可以在为每个请求建立一个线程执行. 任务是一组逻辑工作单元,而线程则是使任务异步执行的机制. 一个简单的例子:当我们定义了一个Runnable对象时,即任务,使用new Thread(Runnable).start

Java多线程——锁概念与锁优化

为了性能与使用的场景,Java实现锁的方式有非常多.而关于锁主要的实现包含synchronized关键字.AQS框架下的锁,其中的实现都离不开以下的策略. 悲观锁与乐观锁 乐观锁.乐观的想法,认为并发读多写少.每次操作的时候都不上锁,直到更新的时候才通过CAS判断更新.对于AQS框架下的锁,初始就是乐观锁,若CAS失败则转化为悲观锁. 悲观锁.悲观的想法,认为并发写多读少.每次操作数据都上锁,即使别人想读也要先获得锁才能读.对于1.6以前的synchronized关键字,则是悲观锁的实现之一.

mybatis中java自动化生成dao层简单实用

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > &

Java多线程&amp;lt;1&amp;gt;

1.Java多线程的概念: 线(Thread):它指的是一个任务的从开始执行流程到结束. 穿线提供执行任务的机构.供java条款.在一个程序可以启动多个并发线程.候执行. 在单处理器系统中,多个线程共享CPU时间,而操作系统负责调度及分配资源给它们.当程序作为一个应用程序来执行时.JAVA解释器为main开启一个线程. 当程序为Applet执行时,Web浏览器启动一个线程来执行applet.还能够在程序中创建附加的线程以执行并发任务. 在JAVA中,每一个任务都是Runnable的实例. 2.创