线程:线程(Thread)是控制线程(Thread of Control)的缩写,它是具有一定顺序的指令序列(即所编写的程序代码)、存放方法中定义局部变量的栈和一些共享数据。线程是相互独立的,每个方法的局部变量和其他线程的局部变量是分开的,因此,任何线程都不能访问除自身之外的其他线程的局部变量。如果两个线程同时访问同一个方法,那每个线程将各自得到此方法的一个拷贝。
Java中线程的实现通常有两种方法:对Thread类进行派生并覆盖run方法(继承Thread类并覆盖Thread类的run方法完成线程类的声明);另一种是通过实现Runnable接口
1、派生Thread类并重载run方法
多线程:
程序运行结果:
2、实现Runable接口创建线程:
单线程:
多线程:
程序运行结果:
线程生命周期:
线程的4种状态:线程创建、可运行状态、不可运行状态、退出
创建(new)状态:调用new方法产生一个线程对象后、调用start方法前所处的状态。还没有调用 start方法是无法启动的,也无法执行。当线程处于创建状态时,线程对象可以调用start方法进入启动状态,也可以调用stop方法进入停止状态。
可运行(runnable)状态:当线程对象执行start()方法后,线程就转到可运行状态。进入些状态只是说明线程对象具有了可以运行的条件,但线程并不一定处于运行状态。因为在单处理器系统中运行多线程程序时,一个时间点只有一个线程运行,系统通过调试机制实现宏观意义上的运行线程共享处理器。因此一个线程是否在运行,除了它必须处于Runnable状态之外,还取决于优先级和调试。
不可运行(non Runnable)状态:线程处于不可运行状态 是由于线程被挂起或者发生阻塞,例如对一个线程调用wait()函数后,它就进入阻塞状态;调用线程的notify或notifyAll方法后它才能再次回到可执行状态。
v退出(done)状态 :一个线程可以从任何一个状态中调用stop方法进入退出状态。
线程一旦进入退出状态就不存在了,不能再返回到其他的状态。除些之外,如果线程执行完run方法,也会自动进入退出状态。
线程的创建和启动:
Java利用Thread来创建线程,通过线程类的构造方法创建一个线程,并通过调用start方法启动该线程,启动线程的目的是为了执行它的run()方法,而Thread类中默认的run()方法没有任何可操作代码,所以必须重新定义run()方法。java通过两种重新定义run()方法的方式:
1、派生线程类Thread的子类
2、实现Runnable的接口
线程在调用start()方法之后,系统会自动调用run()方法。start()方法被调用之后,系统会得知线程准备完毕并且可以执行run()方法,start()方法就返回了,start()方法不会等待run()方法执行完毕。
线程状态转化:
1、以下几种情况发生时,线程进入可执行状态:
(1)其他线程调用notify()或者notifyAll()方法,唤起处于不可执行状态的线程。
public final void notify() //仅仅唤醒一个线程并允许它获得锁
public final void notifyAll() //唤醒所有等待这个对象的线程,并允许它们获得锁
wait和notify是Java同步机制的重要内容。
(2)线程调用sleep(millis)方法,millis毫秒之后线程会进入可执行状态,在millis毫秒内让当前正在执行的线程进入休眠状态,时间到了后自动苏醒并继续执行。
static void sleep(long millis,int nanos(纳秒)) throws InterruptedException
(3)线程对I/O操作的完成
2、以下情况线程进入不可执行状态
(1)线程自动调用wait()方法,等待某种条件的发生
public final void wait() throws InterruptedException
(2)线程调用 sleep()方法进入不可执行状态,在一定时间后会进入可执行状态。
(3)线程等待I/O操作的完成
等待线程结束:
isAlive()方法用来判断一个线程是否存活。
1、当线程处于可执行状态 或者 不可执行状态 时,isAlive()方法返回true;
2、当线程处于创建状态或者退出状态时,则返回false
public final boolean isAlive() //该方法用于测试线程是否处于活动状态(已启动调用 了start方法,且尚未退出所处的状态)
如何判断一个线程是否已经终止??
(1)不断查询 第一个线程是否已经终止,如果没有,则让主线程睡眠一直到它终止即“while/isAlive/sleep”
线程1.start();
while(线程1.isAlive()){
Thread.sleep(休眠时间);
}
线程2.start();
(2)利用join()方法
public final void join(long millis,int nanos) throws InterruptedException
等待该线程终止的时间最长为毫秒(millis)加纳秒(nanos),为0时表示一直等待下去
public final void join() throws InterruptedException
等待该线程终止
Mthod2()
这一次为什么没有等th1结束等1s再执行th2呢?
线程调度:
多线程应用程序的每一个线程的重要性和优先级可能不同。多个线程都在等待获得CPU的时间片,优先级高的线程能抢占CPU并得以执行;当多个线程交替抢占CPU时,优先级高的线程占用的时间应该多。
Java中CPU的使用通常是抢占式调试模式(是指多线程处于可运行状态,但只有一个线程正在运行,当线程一直运行直到结束,或者进入不可运行状态,或者具有更高优先级的线程变为可运行状态,它将会让出CPU)不需要时间片分配 里程。
设置线程优先级:public final void setPriority(int newPriority)
newPriority的值必须在MIN_PRIORITY到MAX_PRIORITY范围内,通常它们的值分别1和10.目前Windwos系统只支持3个级别的优先级,它们分别是Tread.MAX_PRIORITY、Thread.MIN_PRIORITY和Thread.NORM_PRIOPRITY
获取当前线程优先级:public final int getPriority()
改变优先级:
线程同步:
在线程异步模式的情况下,同一时刻有一个线程在修改共享数据,另一个线程在读取共享数据,当修改共享数据的线程没有处理完毕,读取数据的线程肯定会得到 错误的结果。如果采用多线程的同步控制机制,当处理共享数据的线程完成 处理数据之后,读取线程读取数据。
异步模式的例子:
上面由于线程不同步导致的错误,Java引入了“锁”的机制。
锁:每个线程进入共享代码之前获得锁。否则不能进入共享代码区,并且在退出共享代码之前释放该锁,这样就能达到线程同步的目的。
关键字:synchronized声明的方法为同步方法。Java有一个专门负责管理线程对象中同步方法访问的工具——同步模型监视器,它的原理是为每个具有同步代码的对象准备一把惟一的“锁”。当多个线程访问对象时,只有取得锁的线程才能进入同步 方法,其他访问共享对象的线程停留在对象中等待,如果获得锁的线程调用 wait方法放弃锁,那么其他 等待的线程将有机会获得锁。当某个等待线程取得 锁,它将执行同步 方法,而其他没有取得 锁的线程仍然继续等待获得锁。
Java中线程之间通过 消息实现相互通信,wait()、notify()及notifyAll()方法可完成线程间的消息传递。
如:一个对象包含一个synchonized同步方法,同一时刻只能有一个获得锁的线程访问该对象中的同步 方法,其他 线程被阻塞在对象 中等待获得锁。当线程调用 wait()方法可使该线程进入 阻塞状态,其他线程调用notify()或notifyAll()方法可以唤醒该线程。
死锁:由于两个线程都 在等待对方释放各自拥有的锁的现象称为死锁。这种现象往往是由于相互嵌套的synchronized代码段而造成,因此,在程序中尽量少用嵌套的synchronized代码块。
线程通信:Java中线程之间的通信是通过Object类中的wait()、notify()、notifyAll()等几种方法实现的。Java中每个对象内部不仅有一个对象锁之外,还有一个线程等待队列,这个队列用于存放所有等待对象锁的线程。