在多任务操作系统中,多进程实现了多任务的并发执行,进程的多线程实现了进程中代码块(线程)的并发执行。
例如:多进程:我要使用qq聊天,又要使用firefox进行上网,还要进行java编程,这儿进启动了三个进程,实现3我要的三个任务,并且同时执行中。
例如:多线程:当用qq聊天的时候,我们同时和5个人进行聊天,那么有6(qq本身为一个线程)个对应的线程,同时进行处理5人同时聊天。
进程:运行中的程序,或者是执行的一组代码集合。
线程:程序的一个功能,进程中的一个任务,或进程中的一个代码流程块(执行顺序不固定)。
当我们运行一个应用程序的时候,启动进程(对应一个或多个),操作系统给进程分配资源{内存(程序运行)资源},进程再将资源分配给线程,线程完成具体任务,可以认为进程是线程的容器。进程是系统资源分配的单位,包括1到多个线程;线程是进程独立调度和分派的基本单位,共享进程资源。
线程为了程序任务并发执行,提高资源的利用率和系统吞吐量,减少程序并发执行时付出的时空开销。
当线程启动,操作系统不会给线程分配内存,是让线程共享原有的进程块的内存,所有,线程间通信很容易,速度快。
进程都处在不同的内存快,通信比较困难。
Java应用程序中,JVM内部的多任务是通过线程实现的,JVM负责线程的调度。
JVM调度方式为抢占式调度,根据线程的优先级来获取CPU的使用权。
注:分时调度,所有的线程会轮流的获得CPU使用权,并平均分配线程占用CPU的时间。
并发执行,是宏观上的同时(计算机表现),微观上还是顺序执行(异步)。
CPU时钟频率:每秒钟执行cpu指令的个数。在每个时钟周期内,CPU实际上去执行一条或者多条指令。操作系统将进程线程进行管理,无顺序的轮流分配每个进程一小段(短)的时间(不一定均分),然而在每个线程内部,程序的代码自己处理该进程内部线程的时间分配,多个线程间相互切换执行(切换时间极短)。
java应用执行,将启动JVM进程,在JVM中只有JVM一个进程,JVM环境中所有程序代码运行以线程运行。
java语言内置多线程功能的支持,实现方式有:
①继承Thread类,覆盖它run()方法;注:Thread类实现Runnable接口
区别:无法继承其他父类
②实现Runnable接口,实现run()方法。
区别:继承其他类,多线程可共享同一个thread对象。
线程不会自动启动,要调用start()方法(Thread类的)启动,JVM调用该线程run()方法。
启动线程的时候,先通过Thread类的public Thread(Runnable target) 构造方法,构造出Thread对象,然后调用Thread对象的start()方法启动线程(run()方法代码执行)。
继承Thread类或实现Runnable接口来实现多线程,最终还是使用Thread对象的API来操作线程的,所以Thread类的API很重要。
代码示例:
package ca.threadsyn; import java.util.concurrent.locks.LockSupport; public class DuplexThread { public static void main(String[] args) { //继承Thread类的线程使用 Thread1 t1 = new Thread1(); t1.start(); //实现Runnable接口的线程使用 Thread2 t2t = new Thread2(); Thread t2 = new Thread(t2t); t2.start(); } } //继承Thread类,覆盖它run()方法 class Thread1 extends Thread{ @Override public void run() { while(true){ try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("11111"); } } } //实现Runnable接口,实现run()方法 class Thread2 implements Runnable{ @Override public void run() { while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("22222"); } } }
输出结果,thread1和thread2切换执行。
11111
22222
22222
11111
22222
11111
11111
22222
......
线程操作方法:
java中线程状态可以通过程序控制,对应了一个方法(java.lang.*),线程状态操作包括启动(start)、终止(stop)、睡眠(sleep)、挂起 (suspend)、恢复(resume)、等待(wait)和通知(notify)。
创建新的Thread对象(构造方法):
Thread()
Thread(Runnable target)
Thread(String name)
Thread(Runnable target, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, String name)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name,long stackSize)
* group线程组;target为run()方法被调用的对象(调用谁的run就是谁);name新线程名称;stackSize新线程所需的堆栈stack大小,如为0则该参数无效
获取当前正在运行的线程对象的引用:staticThread currentThread()
获取当前线程的Id:long getId()
获取当前线程名称:final String getName()
获取当前线程的优先级:final int getPriority() 注: *下10有注解。
获取当前线程的状态:State getState() 注: *State为枚举enum:下6有注解。
获取当前线程所属的线程组: final ThreadGroup getThreadGroup()
获取线程的上下文ClassLoader:ClassLoader getContextClassLoader()
测试当前线程在指定对象上是否保持监视器锁:static native boolean holdsLock(Object obj) *当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
测试当前线程是否已经中断:static boolean interrupted()
测试当前线程是否处于活动状态:final native boolean isAlive()
测试当前线程是否为守护线程:final boolean isDaemon()
测试线程是否线程是否已经中端:boolean isInterrupted()
设置线程名称为指定名称:final void setName(String name)
启动线程:void start()
中断线程:void interrupt()
存放线程体代码:void run( )
线程休眠:Thread.sleep(millisecond) *正在执行的线程休眠(暂停执行)
设置优先级:setPriority(p)
线程联合:join() *等待该线程终止
设置线程的上下文ClassLoader :void setContextClassLoader(ClassLoader cl)
守护线程设置:final void setDaemon(boolean on)
线程的toString()方法: String toString()*返回线程名称、优先级和线程组名(组为空,则无此项)
方法yield() :static native void yield() *暂停正在执行的线程,但仍处于可运行状态,把执行机会让给相同或者更高优先级的线程,可以和其他的等待执行的线程竞争处理器资源执行
方法setDefaultUncaughtExceptionHandler():static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) :设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。
方法setUncaughtExceptionHandler:void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) :设置该线程由于未捕获到异常而突然终止时调用的处理程序
注意:Thread中suspend()、resume()、stop()方法,在JDK1.5中已经废除,因为有死锁倾向,不安全性。
引用:Thread.stop 来终止线程将释放它已经锁定的所有监视器(作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果)。如果以前受这些监视器保护的任何对象都处于一种不一致的状态,则损坏的对象将对其他线程可见,这有可能导致任意的行为。stop 的许多使用都应由只修改某些变量以指示目标线程应该停止运行的代码来取代。目标线程应定期检查该变量,并且如果该变量指示它要停止运行,则从其运行方法依次返回。如果目标线程等待很长时间(例如基于一个条件变量),则应使用 interrupt 方法来中断该等待。该方法的附加危险是它可用于生成目标线程未准备处理的异常(包括若没有该方法该线程不太可能抛出的已检查的异常)。
suspend()该方法已经遭到反对,因为它具有固有的死锁倾向。如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源。如果重新开始目标线程的线程想在调用 resume 之前锁定该监视器,则会发生死锁。这类死锁通常会证明自己是“冻结”的进程。
注:
4:join()方法:阻塞调用线程,执行被调用线程,该线程执行完再执行调用线程
5:sleep()方法、wait()方法、yield()方法区别:
sleep()方法属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu使用权给其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
Thread类的sleep()方法和yield()方法;
暂停后的状态:sleep()方法,进入被阻塞的状态,直到经过指定时间后,才进入可运行状态。yield()方法,直接将当前线程转入可运行状态。
没有其他等待运行的线程:sleep()方法,当前线程会继续等待指定的时间。yield()方法,当前线程会马上恢复执行。
等待线程的优先级别:sleep()方法,不考虑,机会均等。yield()方法将优先级相同或更高的线程运行。
6:线程状态:
新建、就绪、运行、阻塞、死亡(终止)
在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。
Thread.State.NEW 新建 线程尚未运行 刚new出一个Thread对象,未调用start()方法
RUNNABLE 可运行(就绪或运行) 可运行状态 已经调用start()方法,除了CPU之外,运行的所有资源已经就绪,获取CPU使用权,就开始运行
阻塞 线程状态监视器锁阻塞一个线程等待,因为某种原因放弃了CPU使用权,暂时退出运行状态,有以下三种:
①: 等待阻塞:运行程序执行wait()方法,JVM把该线程放入等待池中。
②: 同步阻塞:运行线程获取操作对象的同步锁时,若该对象的同步锁被别的线程占用,则JVM把该线程放入锁池中。
③: 其他阻塞:运行的线程执行sleep()方法或join()方法,或发出了I/O请求时,JVM把该线程设置为阻塞状态。当sleep()超过设定时间,join()等待线程终止或超过设定时间,或I/O处理完毕时,线程重新转入就绪状态。
BLOCKED 运行程序调用Object的wait()方法后。受阻塞并且正在等待监视器锁的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用Object 的wait()方法 之后再次进入同步的块/方法。
WAITING 等待线程的线程状态。运行调用Obeject的wait()方法,或者Thread的join()方法,参数没有设定超时时间(long time),并且等待没有超时。LockSupport的part()方法,处于等待状态的线程正等待另一个线程,以执行特定操作。例如,一个线程称为Object.wait()对象正在等待另一个线程调用Object.notify()或Object.notifyAll()对象。Thread.join()的一个线程正在等待指定的线程终止。
TIMED_WAITING 指定等待时间的某一等待线程的线程状态(定时等待状态的线程)。调用:
Thread类的sleep(long millis)或(long millis, int nanos)
Thread类的join(long time)或(long millis, int nanos)
Object类的wait(long timeout)或(long timeout, int nanos) timeout最大等待(睡觉)时间,以毫秒为单位,nanos额外的时间,在纳秒范围0-999999
LockSupport类的parkNanos(long nanos) nanos最大等待纳秒数
LockSupport类的parkUntil(long deadline) deadline最后期限的绝对时间,毫秒为单位
TERMINATED 死亡 执行线程完成或异常退出。线程的run方法运行完毕或者在运行中出现未捕获的异常时。
7:start():调用start()方法启动线程后,并不立即执行多线程的代码(run()内代码),而是该线程变为可运行状态(Runnable),执行时候由CPU决定。
8:run():实现Runnable接口,使得类有了多线程的特征,run()方法是多线程程序的一个约定,所有的多线程代码都在run()方法里。
9:sleep():sleep的目的:不让当前的线程独占该进程获取的CPU资源,把执行机会给其他线程。
10:线程的优先级:
默认情况下,一个线程继承父线程的优先级(具有继承关系)。
优先级影响CPU在线程间切换,切换的原则是:
当一个线程通过显式放弃、睡眠或者阻塞、自愿释放控制权时,所有线程均接受检查而优先级高线程将会优先执行;
一个线程可以被一个高优先级的线程抢占资源;
同级别的线程之间,则通过控制权的释放,确保所有的线程均有机会运行。
java提供了10个线程的优先级别,使用整数表示(1到10),但与常见的操作系统都不能很好的映射,为了保持操作系统间移植性,使用以下三个静态常量作为优先级设定,这 样可以保证同样的优先级采用同样的调度方式。
Thread类下:
public final static int MAX_PRIORITY = 10;最高优先级;
public final static int NORM_PRIORITY = 5;默认优先级;
public final static int MIN_PRIORITY = 1;最低优先级。