线程学习笔记

来源:互联网 发布:directx for windows 编辑:程序博客网 时间:2024/06/07 00:23

线程是程序中一个单一顺序控制流程进程内一个相对独立的、可调度执行单元,是系统独立调度分派CPU的基本单位指令运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

适用范围:

服务器中的文件管理或通信控制

前后台处理

异步处理

 

线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程不拥有系统资源,只有运行必须的一些数据结构;它与父进程的其它线程共享该进程所拥有的全部资源。线程可以创建和撤消线程,从而实现程序的并发执行。一般,线程具有就绪、阻塞和运行三种基本状态。

在多中央处理器的系统里,不同线程可以同时不同的中央处理器运行甚至当它们属于同一个进程时也是如此。大多数支持多处理器的操作系统都提供编程接口来让进程可以控制自己的线程与各处理器之间的关联度(affinity)。

有时候,线程也称作轻量级进程。就象进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。它们共享内存、文件句柄和其它每个进程应有的状态。

进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。一个进程中的多个线程共享相同的内存地址空间,这就意味着它们可以访问相同的变量和对象,而且它们从同一堆中分配对象。尽管这让线程之间共享信息变得更容易,但您必须小心,确保它们不会妨碍同一进程里的其它线程。

Java 线程工具和 API看似简单。但是,编写有效使用线程的复杂程序并不十分容易。因为有多个线程共存在相同的内存空间中并共享相同的变量,所以您必须小心,确保您的线程不会互相干扰。

为了正确有效地使用线程,必须理解线程的各个方面并了解Java 实时系统。必须知道如何提供线程体线程的生命周期实时系统如何调度线程线程组、什么是幽灵线程(Demo nThread)。

 

幽灵线程:任何一个Java线程都能成为幽灵线程。它是作为运行于同一个进程内的对象和线程的服务提供者。例如,HotJava浏览器有一个称为" 后台图片阅读器"的幽灵线程,它为需要图片的对象和线程从文件系统或网络读入图片。 幽灵线程是应用中典型的独立线程。它为同一应用中的其他对象和线程提供服务。幽灵线程的run()方法一般都是无限循环,等待服务请求。

线程组:每个Java线程都是某个线程组的成员。线程组提供一种机制,使得多个线程集于一个对象内,能对它们实行整体操作。譬如,你能用一个方法调用来启动或挂起组内的所有线程。Java线程组由ThreadGroup类实现。当线程产生时,可以指定线程组或由实时系统将其放入某个缺省的线程组内。线程只能属于一个线程组,并且当线程产生后不能改变它所属的线程组。

 

 

 

 

 

线程体:所有的操作都发生在线程体中,在Java中线程体是从Thread类继承的run()方法,或实现Runnable接口的类中的run()方法当线程产生并初始化后实时系统调用它的run()方法。run()方法内的代码实现所产生线程的行为,它是线程的主要部分。

线程状态:线程在它的生命周期内的任何时刻所能处的状态以及引起状态改变的方法


新线程态(New Thread)产生一个Thread对象就生成一个新线程。当线程处于"新线程"状态时,仅仅是一个空线程对象,它还没有分配到系统资源。因此只能启动或终止它。任何其他操作都会引发异常。例如,一个线程调用了new方法之后,并在调用start方法之前的处于新线程状态,可以调用start和stop方法。

可运行态(Runnable)start()方法产生运行线程所必须的资源,调度线程执行,并且调用线程的run()方法。在这时线程处于可运行态。该状态不称为运行态是因为这时的线程并不总是一直占用处理机。特别是对于只有一个处理机的PC而言,任何时刻只能有一个处于可运行态的线程占用处理机。Java通过调度来实现多线程对处理机的共享。注意,如果线程处于Runnable状态,它也有可能不在运行,这是因为还有优先级和调度问题。

阻塞/运行态(NotRunnable)当以下事件发生时,线程进入非运行态。

当以下事件发生时,线程进入非运行态。

①suspend()方法被调用;

②sleep()方法被调用;

③线程使用wait()来等待条件变量;

④线程处于I/O请求的等待。

 

死亡态(Dead)当run()方法返回,或别的线程调用stop()方法,线程进入死亡态。通常Applet使用它的stop()方法来终止它产生的所有线程。


不可运行状态:

sleep()方法

线程调用wait()方法等待特定条件的满足

线程输入/输出阻塞(IO情况)

返回可运行状态:

处于睡眠状态的线程在指定的时间过去后

如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变

如果线程是因为输入/输出阻塞,等待输入/输出完成

 

 

 

 

 

 

 

 

线程就是程序中单独顺序的流程控制。线程本身不能运行,只能用于程序中。是程序内的顺序控制流,只能使用分配给程序的资源和环境。

多线程编程的目的,就是“最大限度的利用cpu资源”,当某一线程的处理不需要占用cpu而只和I/O等资源打交道时,让需要占用cpu资源的其它线程有机会获得cpu资源。从根本上说,这就是多线程编程的最终目的。

当程序启动运行时,就自动产生一个线程,主方法main就在这个主线程上运行。

我们的程序都是由线程来执行的。

 

进程与现成的区别:

多个进程的内部数据和状态都是完全独立的,而多个线程是共享一块内存和一组系统资源,有可能互相影响

线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以现场的切换比进程切换的负担要小

进程就是运行中的一个程序。

 

一个进程可以包含一个或多个线程

一个程序实现多个代码同时交替运行就需要产生多个线程

cpu随机的抽出时间,让我们的程序一会做这件事情,一会做另外一件事情。

 

多线程程序比多进程程序需要更少的管理费用。进程是重量级的任务,需要分配给他们独立的地址空间。进程间通信时昂贵和受限的。进程间的转换也是很需要花费的。另一方面,线程是轻量级的选手。他们共享相同的地址空间并且共同分享同一个进程。线程间通信是便宜的,线程间的转换也是低成本的。

 

多线程可帮助你编写出CPU最大利用率的高效程序,使得空闲时间保持最低。这对Java运行的交互式的网络互连环境是至关重要的,因为空闲时间是公共的。例如:网络的数据传输率远低于计算机处理能力,而本地文件系统资源的读写速度也远低于CPU的处理能力。当然,用户输入也比计算机慢很多。在传统的单线程环境中,程序必须等待每一个这样的任务完成以后才能执行下一步------尽管CPU有很多空闲时间。多线程使你能狗获得并充分利用这些空闲时间。

 

Java线程模型:

Java多线程的优点就在于取消了主循环/轮询机制。一个线程可以暂停而不影响程序的其他部分。例如:当一个线程从网络读取数据或等待用户输入时产生的空闲时间可以被利用到其他地方。多线程允许活的循环再每一帧间隙中沉睡一秒而不暂停整个系统。

 

Java运行系统在很多方面依赖于线程,所有的类库设计都考虑到多线程。实际上,Java使用线程来使整个环境异步。这有利于通过防止CPU循环的浪费来减少无效部分。

 

为更好的理解多线程环境的优势,我们可以将它与它的对照物相比较。单线程系统的处理途径是使用一种叫做轮询的时间循环方法。在该模型中,单线程控制在一个无限循环中运行,轮询一个事件序列来决定下一步做什么。一旦轮询装置返回信号表明已准备好读取网络文件,事件循环调度控制管理到适当的事件处理程序。知道事件处理程序返回,系统中没有其他事件发生。这就浪费了CPU时间。这导致了程序的一部分独占了系统,阻止了其他事件的执行。总得来说,单线程环境,当一个线程因为等待资源时阻塞(block,挂起执行),整个程序停止运行。

 

在java中通过run方法为线程指明要完成的任务,有两种技术来为线程提供run方法。

1, 继承Thread类并重写run方法

2, 通过定义实现Runnable接口的类进而实现run方法。

 

 

Threa类文档说明:

A thread is a thread of execution in aprogram. The Java Virtual Machine allows an application to have multiplethreads of execution running concurrently.

一个线程在一个程序中是一个线程执行体。JVM允许一个应用有多个并发运行的线程执行体。

Every thread has a priority. Threads withhigher priority are executed in preference to threads

每一个线程有一个优先级。高优先级的线程优先于低优先级的线程执行。

with lower priority. Each thread may or maynot also be marked as a daemon. When code

每一个线程可能会或可能不会被标记为一个守护进程(后台线程)

running in some thread creates a new Threadobject, the new thread has its priority initially

当代码运行在一个线程的时候会创建一个线程对象。

set equal to the priority of the creatingthread, and is a daemon thread if and only if the creating thread is a daemon.

这个线程有它自己的优先级,默认等于创建它的这个线程的优先级。如果是一个守护线程,那么这个新创建的线程也是一个守护线程。

When a Java Virtual Machine starts up,there is usually a single non-daemon thread (which typically calls the methodnamed main of some designated class).

当一个虚拟机启动运行时,通常有一个非守护线程(通常调用命名为指定类的main)的方法。

The Java Virtual Machine continues toexecute threads until either of the following occurs:

java虚拟机继续执行线程,直到发生以下情况

The exit methodof class Runtime has been called and the security manager has permitted theexit operation to take place.

已调用类运行时的退出方法,安全管理器允许退出操作发生。

All threads thatare not daemon threads have died, either by returning from the call to the runmethod or by throwing an exception that propagates beyond the run method.

不是守护线程的所有线程都已死亡,要么从调用返回到run方法,要么抛出一个异常,该异常传播到run方法之外。

There are two ways to create a new threadof execution.

创建新的执行线程有两种方法

One is to declare a class to be a subclassof Thread. This subclass should override the run method of class Thread.

一个是去声明一个类去作为一个线程的子类。这个子类应该重写父类线程的run方法。

An instance of the subclass can then beallocated and started.

然后这个子类的一个实例可以分配资源和启动。

The other way to create a thread is todeclare a class that implements the Runnable interface.

另外一种创建线程方法是去声明一个实现了Runnable接口的类。

That class then implements the run method.

这个类实现了run方法。

An instance of the class can then beallocated, passed as an argument when creating Thread, and started.

然后可以分配类的实例,在创建线程时作为参数传递,并启动。

 

 

Constructor(构造):

Thread()

Thread(Runnable target)

Thread(Runnable target, String name)

Thread(String name)

Thread(ThreadGroup group, Runnable target)

Thread(ThreadGroup group, Runnable target,String name)

Thread(ThreadGroup group, Runnable target,String name, long stackSize)

Thread(ThreadGroup group, String name)

 

Method Summary(方法简介)

run():run方法是我们线程执行的真正的代码区域,所有任务放在run方法中去执行

If this thread was constructed using aseparate Runnable run object,

如果线程被构造使用一个单独的Runnable对象

then that Runnable object's run method iscalled; otherwise,

然后接下来这个Runable对象的run方法被调用执行,否则,

this method does nothing and returns.

这个方法什么也不做和返回。

Subclasses of Thread should override thismethod.

Thread的类的子类应该重写这个方法。

start():启动线程的唯一的方法(不能直接调用run方法,而是调用这个start方法,让虚拟机去执行调用run方法,也就是说start方法会让线程去执行(先获取线程执行所需资源),同时调用线程的run方法)

Causes this thread to begin execution;

导致这个线程开始执行;

the Java Virtual Machine calls the runmethod of this thread.

JVM执行这个线程的run方法。

It is never legal to start a thread morethan once.

启动线程多次是不合法的。

In particular, a thread may not berestarted once it has completed execution

特别是,线程在完成执行后可能不会重新启动。

 

对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)

对于双核或双核以上的CPU来说,可以真正做到微观并行。

 

Thread类实现了Runnable接口,然后里面有一个Runnable实例属性。Thread类的构造中有接受Runnable实例的构造方法。通过此构造给Runnable实例属性赋值。然后run方法中判断这个属性实例,如果有值,就调用这个属性实例(就是构造中传入的Runnable实例)的run方法,否则执行Thread中的run方法。


线程的默认命名:


当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:

Thread-number,该number将是自动增加,并被所有的Thread对象所共享(因为它是static的成员变量)。

public synchronized void start() {

       if (threadStatus != 0)

           throw new IllegalThreadStateException();

       group.add(this);

       boolean started = false;

       try {

            start0();

           started = true;

       } finally {

           try {

                if (!started) {

                   group.threadStartFailed(this);

                }

           } catch (Throwable ignore) {

                /* do nothing. If start0 threwa Throwable then

                  it will be passed up the callstack */

           }

       }

}

在start方法中无法显示的看到调用run()方法,那么只能是在start0()方法中调用了。

而这个start0()方法是一个native修饰的方法,就是底层c语言操作的。

 

 

停止线程:线程的消亡不能通过调用一个stop()命令,而是让run()方法自然结束。

public classMyThread implements Runnable{

       private boolean flag = true ;      

       @Override

       public void run() {

              while(flag){

                     //......

              }

       }     

       public void stopRunning(){

              flag=false;

       }

}

classControThread {

       private MyThread r = new MyThread();

       private Thread t = new Thread(r);     

       public void startThread(){

              t.start();

       }     

       public void stopThread(){

              ((MyThread) r).stopRunning();

       }

}

给了一个标志量,绝对不能使用stop()方法。

 

线程的调度策略:

线程调度器选择优先级最高的线程运行。但是,如果发生一下情况,就会终止线程的运行。

线程体中调用了yield()方法,让出了对CPU的占用权

线程体中调用了sleep()方法,使线程进入睡眠状态

线程由于I/O操作而受阻塞

另一个更高优先级的线程出现

在支持时间片的系统中,该线程的时间片用完

 

 

 

 

 

为什么要引入同步机制:

在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源。必须对这种潜在资源冲突进行预防。

解决方法:在线程使用一个资源时为其枷锁即可。访问资源的第一个线程为其加上锁以后,其他线程便不能再使用那个资源,除非被解锁。

 

关于成员变量与局部变量:

如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。

如果一个变量是局部变量(即变量在run方法内),那么每一个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。

 

synchronized关键字:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。Java里面每一个对象都有一把锁(lock或者监视器monitor)。当一个方法被修饰为synchronized的时候,当一个线程去访问这个方法的时候(表示将该对象上锁),这个线程就会给这个方法所在的那个对象上锁。此时其他任何线程都无法再次访问该synchronized方法了。当这个线程执行完了或者是执行的过程中抛出了异常。这时就会将这个锁释放掉。第二个线程就有可能再次可以进入对象,执行具体的方法了。并且继续的给对象上锁。

 

如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized的方法。

方法加上static后,synchronized锁的就不是当前对象了,而是锁了当前那个对象所对应的class对象。

如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的class对象,因为Java中无论一个类有多少个对象,这些对象对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。

 

关键在于锁的是谁

 

synchronized块

synchronized(对象){}这里里面的对象任意对象都可,仅仅相当于一个标识。(表示对制定的对象上锁)

其实和synchronized一样。

synchronized块是细粒度的,只会将块中的代码同步,位于方法内,synchronized块之外的代码是可以被多个线程同时访问到。所以,一般都是使用synchronized块。

Synchronized是粗粒度的,会把方法的所有部分访问。某一时刻,只能有一个线程执行该synchronized方法;

 

 

 

 

多线程的同步:如何实现同步

线程间的相互作用:

wait和notify

The pools:

       waitpool

       lockpool

 

死锁(deadlock):

线程与线程之间的协调:

 

Object中的wait():publicfinal void wait() throws InterruptedException(final修饰的,不可重写的,注意)

Causes the current thread to wait untilanother thread invokes the notify() method or the notifyAll() method for thisobject.

(当调用了对象的wait)导致当前的线程去等待直到其他线程执行这个对象的nofify()方法或者nofifyAll()方法

In other words, this method behaves exactlyas if it simply performs the call wait(0).

换句话说,这个方法完全类似于执行了wait(0)

The current thread mustown this object's monitor.

当前的线程必须拥有这个对象的锁。(也就是说这个wait()方法一定在synchronized块或者synchronized修饰的方法里面)

The thread releases ownership of thismonitor and waits until another thread notifies threads

线程会释放这个锁的拥有权并且等待着,直到另外的线程通知所有

waiting on this object's monitor to wake upeither through a call to the notify method or the

等待这个对象锁被唤醒的其他线程。这个唤醒方法是notify()或nofifyAll()。

notifyAll method. The thread then waitsuntil it can re-obtain ownership of the monitor and

resumes execution.

接下来这个线程等待直到它能重新获取了这个锁的拥有权并且继续的执行。

 

线程获得了对象的锁,然后进入synchronized方法内,然后对象调用wait()方法,这个线程释放自己获得的对象的锁,并且处于等待状态。然后别的线程就有可能拥有这个被释放的对象锁。当另外一个线程调用notify()或notifyAll()的时候,会将等待这个对象锁的所有线程唤醒。线程被唤醒后,并不是立即去执行,而是仍然等待直到这个线程重新获得这个锁的拥有权之后才去继续执行wait()方法后续的代码。

This method should only be called by athread that is the owner of this object's monitor.

这个方法只能被对象锁拥有者的线程调用(位于synchronized里面)。

Nofify():

Wakes up a single thread that is waiting onthis object's monitor.

唤醒一个等待对象锁的线程。

If any threads are waiting on this object,one of them is chosen to be awakened.

如果任何一个线程都处于等待,他们当中的一个会被选择唤醒。

The choice is arbitrary and occurs at thediscretion of the implementation.

这个选择是武断的任意大的由实现来去决定的(唤醒是随机的)。

A thread waits on an object's monitor bycalling one of the wait methods.

一个线程等待对象锁通过调用对象的wait()方法。

The awakened thread will not be able toproceed until the current thread relinquishes the lock on this object.

被唤醒的线程将无法继续进行,直到当前线程放弃此对象上的锁定。

The awakened thread will compete in theusual manner with any other threads that might be actively competing tosynchronize on this object

唤醒线程将以通常的方式与可能在该对象上进行同步竞争的任何其他线程进行竞争

for example, the awakened thread enjoys noreliable privilege or disadvantage in being the next thread to lock thisobject.

例如,唤醒线程在锁定该对象的下一个线程中没有可靠的特权或缺点。

This method should only be called by athread that is the owner of this object's monitor

此方法只能由此对象监视器的所有者的线程调用(就是说这个notify也应该是位于synchronized中)

A thread becomes the owner of the object'smonitor in one of three ways

线程以三种方式之一成为对象监视器的所有者。

By executing a synchronized instance methodof that object通过执行该对象的同步实例方法

By executing the body of a synchronizedstatement that synchronizes on the object通过在对象上同步的同步语句的主体执行

For objects of type Class, by executing asynchronized static method of that class. 对于类型类的对象,通过执行该类的同步静态方法。

原创粉丝点击