由1个以上的线程所构成的程序称为多线程程序。Java语言写成的程序一定是从主程序开始操作,所以必须在程序的某个位置启动新的线程,才能算是真正的多线程程序。
1、 线程启动
1) 利用Thread类的子类
新写一个类,继承自Thread,重写里面的run()方法。在主程序里调用start()方法,就会启动一个线程,并执行run()内的代码。注意,被调用出来的新线程是start方法,而非run()方法。当然也可以调用run()方法,只是这样不会启动新的线程。
调用start方法后,Java执行处理系统就会在背后启动新的线程。再由这个新的线程调用run()方法。调用run()方法时,会有两个操作:
·启动新的线程
·调用run()方法
注意类的实例和线程本身是两个不同的部分。即使建立了类的实例,也还没有启动线程,而且就算线程已近结束,类的实例也不会就这样消失不见。
在主线程内启动两个两个子线程后,即使主线程立即结束,整个程序也不能算做完全结束,一直要等到所有线程都已近结束,程序才会结束。
当所有线程都结束是,程序也就结束了。不过,在判断是否结束是,并不包括daemon thread。也就是说,即使daemon thread还在,只要非daemon thread的线程都已近结束,程序即已近停止。
2) 利用Runnable接口
已实现Runnable接口的类必须实现run()方法。其过程如下:建立一个实现了Runnable接口的类,将该类实例传给Thread的构造器,调用start()方法。
Thread类本身也实现了Runnable接口,也有run()方法。只不过Thread类的run()方法为空,没有执行任何部分。Thread类的run()方法通常都是被子类覆盖的。
2、 线程的暂时停止
利用Thread类的sleep()方法即可以暂时停止线程的执行操作。sleep()方法在调用时被放在try-catch语句里面,这是因为sleep()方法可能会抛出也给InterruptedException的异常,这是用在取消线程处理时的异常。
利用sleep()方法所设置的暂时停止时间并不是很精密,所以不太适合用在实时控制的方面。若利用以下的语法结构,则可以ns(10^-9)为单位设置停止时间。
Thread.sleep(ms,ns);
不过一般的Java处理系统并不需要用到这么精密的控制方式。实时上,控制的精度有多高要依Java处理系统而定。
如果要唤醒被Thread.sleep暂停的线程,则可利用interrupt方法。
3、 线程的共享互斥:
1) sychronized
当一个方法加上关键字sychronized声明后,就可以让1个线程操作这个方法。这里指每次只有一个线程可以执行这个方法,这个方法有被称为同步方法。非sychronized就没有这个限制同时可供2个以上的线程执行。
一旦一个线程执行sychronized方法就获得一把锁,可以防止其他线程进入;当执行结束时,锁就释放了。当锁释放后,刚才因为所定而不得其门而入的多个线程便开始抢夺锁定,一定会有一个线程获得锁定。在同一时间点获取锁定的也只有一个线程,若等待的线程不止一个,没抢到得线程就得继续等候。
线程共享互斥的构架称为监视(monitor),而获取锁定时有时也称为持有(own)监视。
2) sychronized阻挡
如果只想启动方法里一部分的线程,而非启动整个方法时,则可使用sychronized阻挡,其格式如下
Sychronized(表达式){
……
}
则需要使用sychronized阻挡
4、 线程的协调
当有一个线程正在执行sychronized方法时,其它线程无法执行该方法。这是简单的型的共享互斥。进一步要求就是“空间是否有空闲”为条件进行线程处理。Java里有wait(),notify()和notifyAll()三个方法可进行这个处理。其中,wait()是让线程乖乖等候的方法,而notify()和notifyAll()则是启动等候中的线程的方法。
1) wait set
所有实例都有一个wait set,wait set是一个在执行该实例的wait方法是、操作停止的线程的集合。一执行wait方法时,线程便会暂时停止操作,进入wait set。只有在下面几种情况才有可能推出wait set。
·有其它线程以notify()方法唤醒该线程;
·有其它线程以notifyAll()方法唤醒该线程;
·有其它线程以interrupt()方法唤醒该线程;
·wait方法已经到期了。
wait set是一个虚拟的概念。它既不是实例的字段,也不是可以获取在实例wait中线程的列表的方法。
使用notify()方法时,有一个线程将会从wait set里退出,被notify唤醒的线程并不是在notify的一瞬间重新开始执行的。这是因为notify()的那一刻,执行notify()的线程还握着锁不放,所以其它线程无法获取该实例的锁。
假设执行notify()方法时,wait set里正在等候的线程不止1个。规格里并没有注明此时该选择哪一个线程。究竟是选择等待线程列表中的第一个,随机选择或是另以其它方式选择,则依Java处理系统的而异。因此在写程序时,程序属性最好不要写成会因所选线程而又所变动。
若没有锁定的线程去调用wait(),notify()或notifyAll()时,便会抛出异常java.lang.IllegalMonitorStateException。
notify()方法和notifyAll()方法两者非常相似,到底该用哪一个?老实说,这个选择有点难。
选择notify的话,因为要唤醒的线程比较少,程序处理速度当然比notifyAll()略胜一筹。
但选择notify()时,若这部分处理得不好,可能会有程序挂掉的危险性。一般来说,选择notifyAll()所写出来的程序代码会比选择notify()可靠。
除非能确定对程序的代码意义一清二楚,否则选择notifyAll()应该稳扎稳打。
1、 线程启动
1) 利用Thread类的子类
新写一个类,继承自Thread,重写里面的run()方法。在主程序里调用start()方法,就会启动一个线程,并执行run()内的代码。注意,被调用出来的新线程是start方法,而非run()方法。当然也可以调用run()方法,只是这样不会启动新的线程。
调用start方法后,Java执行处理系统就会在背后启动新的线程。再由这个新的线程调用run()方法。调用run()方法时,会有两个操作:
·启动新的线程
·调用run()方法
注意类的实例和线程本身是两个不同的部分。即使建立了类的实例,也还没有启动线程,而且就算线程已近结束,类的实例也不会就这样消失不见。
在主线程内启动两个两个子线程后,即使主线程立即结束,整个程序也不能算做完全结束,一直要等到所有线程都已近结束,程序才会结束。
当所有线程都结束是,程序也就结束了。不过,在判断是否结束是,并不包括daemon thread。也就是说,即使daemon thread还在,只要非daemon thread的线程都已近结束,程序即已近停止。
2) 利用Runnable接口
已实现Runnable接口的类必须实现run()方法。其过程如下:建立一个实现了Runnable接口的类,将该类实例传给Thread的构造器,调用start()方法。
Thread类本身也实现了Runnable接口,也有run()方法。只不过Thread类的run()方法为空,没有执行任何部分。Thread类的run()方法通常都是被子类覆盖的。
2、 线程的暂时停止
利用Thread类的sleep()方法即可以暂时停止线程的执行操作。sleep()方法在调用时被放在try-catch语句里面,这是因为sleep()方法可能会抛出也给InterruptedException的异常,这是用在取消线程处理时的异常。
利用sleep()方法所设置的暂时停止时间并不是很精密,所以不太适合用在实时控制的方面。若利用以下的语法结构,则可以ns(10^-9)为单位设置停止时间。
Thread.sleep(ms,ns);
不过一般的Java处理系统并不需要用到这么精密的控制方式。实时上,控制的精度有多高要依Java处理系统而定。
如果要唤醒被Thread.sleep暂停的线程,则可利用interrupt方法。
3、 线程的共享互斥:
1) sychronized
当一个方法加上关键字sychronized声明后,就可以让1个线程操作这个方法。这里指每次只有一个线程可以执行这个方法,这个方法有被称为同步方法。非sychronized就没有这个限制同时可供2个以上的线程执行。
一旦一个线程执行sychronized方法就获得一把锁,可以防止其他线程进入;当执行结束时,锁就释放了。当锁释放后,刚才因为所定而不得其门而入的多个线程便开始抢夺锁定,一定会有一个线程获得锁定。在同一时间点获取锁定的也只有一个线程,若等待的线程不止一个,没抢到得线程就得继续等候。
线程共享互斥的构架称为监视(monitor),而获取锁定时有时也称为持有(own)监视。
2) sychronized阻挡
如果只想启动方法里一部分的线程,而非启动整个方法时,则可使用sychronized阻挡,其格式如下
Sychronized(表达式){
……
}
则需要使用sychronized阻挡
4、 线程的协调
当有一个线程正在执行sychronized方法时,其它线程无法执行该方法。这是简单的型的共享互斥。进一步要求就是“空间是否有空闲”为条件进行线程处理。Java里有wait(),notify()和notifyAll()三个方法可进行这个处理。其中,wait()是让线程乖乖等候的方法,而notify()和notifyAll()则是启动等候中的线程的方法。
1) wait set
所有实例都有一个wait set,wait set是一个在执行该实例的wait方法是、操作停止的线程的集合。一执行wait方法时,线程便会暂时停止操作,进入wait set。只有在下面几种情况才有可能推出wait set。
·有其它线程以notify()方法唤醒该线程;
·有其它线程以notifyAll()方法唤醒该线程;
·有其它线程以interrupt()方法唤醒该线程;
·wait方法已经到期了。
wait set是一个虚拟的概念。它既不是实例的字段,也不是可以获取在实例wait中线程的列表的方法。
使用notify()方法时,有一个线程将会从wait set里退出,被notify唤醒的线程并不是在notify的一瞬间重新开始执行的。这是因为notify()的那一刻,执行notify()的线程还握着锁不放,所以其它线程无法获取该实例的锁。
假设执行notify()方法时,wait set里正在等候的线程不止1个。规格里并没有注明此时该选择哪一个线程。究竟是选择等待线程列表中的第一个,随机选择或是另以其它方式选择,则依Java处理系统的而异。因此在写程序时,程序属性最好不要写成会因所选线程而又所变动。
若没有锁定的线程去调用wait(),notify()或notifyAll()时,便会抛出异常java.lang.IllegalMonitorStateException。
notify()方法和notifyAll()方法两者非常相似,到底该用哪一个?老实说,这个选择有点难。
选择notify的话,因为要唤醒的线程比较少,程序处理速度当然比notifyAll()略胜一筹。
但选择notify()时,若这部分处理得不好,可能会有程序挂掉的危险性。一般来说,选择notifyAll()所写出来的程序代码会比选择notify()可靠。
除非能确定对程序的代码意义一清二楚,否则选择notifyAll()应该稳扎稳打。