一、概念及其理论介绍
1. 线程(轻量级程序)类似于一个程序,也有开始、执行、结束,它是运行在程序内部的一个比进程更小的单元,使用线程的主要应用在于可以在一个程序中同时运行多个任务。每个Java程序都至少有一个线程-主线程。当一个Java程序启动时,JVM会创建主线程,并在该线程中调用程序的main()方法。
2. 多线程就是同时有多个线程在执行。在多CPU的计算机中,多线程的实现是真正的物理上的同时进行。而对于单CPU的计算机而言,实现的只是逻辑上的同时执行,在每个时刻,真正执行的只有一个线程,由操作系统进行线程管理调度,但由于CPU的速度很快,让人柑橘像是多个线程在同时运行。
3. 多线程与多进程的主要区别在于:
进程是一个进程中一段独立的控制流,一个进程可以拥有若干个进程。在多进程设计中各个进程之间的数据块是独立的,一般彼此不影响,要通过信号、管道等进行交流。而多线程设计中各个线程不一定独立,同一任务中的各个线程共享程序 段、数据段等资源。
4.Java中建立线程有两种方法:
一种是继承Thread类,另一种是实现Runnable接口,并通过Thread和实现Runnalbe的类来建立线程。
两种方法的区别:
这两种方法从本质是说是一致的,即都是通过Thread类来建立线程,并运行run()方法。但由于Java不支持多继承,因此,这个线程类如果继承了Thread,就不能再继承其他的类了,通过实现Runnable接口的方法来建立线程,可以在必要的时候继承和业务有关的类,形成清晰的数据类型。
3.Thread类的start()方法将调用run()方法,该方法用于启动线程并执行。但是,start()方法不能多次调用,如不能调用两次thread1.start()方法,否则会抛出一个IlleagalThreadStateException异常。
如:
二、创建线程
在Java中创建线程有两种方法:使用Thread类和使用Runnalbe接口。在使用Runnable接口时需要建立一个Thread实例。因此,无论是通过Thread类
还是Runnable接口
建立线程,都必须建立Thread类或子类的实例。
1.Thread 代码如下:
public class TreadTest {
public static void main(String args[]){
Thread t = Thread.currentThread();
System.out.println("主线程是:" + t);
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2("thread2");
thread1.start();
thread1.start();
thread2.start();
}
}
class Thread1 extends Thread{
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
class Thread2 extends Thread{
public Thread2(String name){
super(name);
}
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
2.创建线程的另一种方式是实现Runnable接口。Runnable接口中只有一个run() 方法,它为非Thread子类的类提供了一种激活方式。一个类实现Runnable接口后,并不代表该类是个“线程”类,不是直接运行,必须通过使用Thread类的实例才能创建线程并运行。
通过Runnable接口创建线程的步骤如下:
1)定义实现Runnable接口的类,并实现该接口中的run()方法;
2)建立一个Thread对象,并将实现Runnable接口的类的对象作为参数传递给Thread类的构造方法;
3)通过Thread类的start()方法启动线程,并运行。
代码如下:
public class RunnableTest {
public static void main(String args[]){
Runable1 ra = new Runable1();
Thread thread = new Thread(ra);
thread.start();
System.out.println("[" + Thread.currentThread().getName() + "]");
for(int i=0;i<30;i++){
System.out.print("C");
}
}
}
class Runable1 implements Runnable{
public void run() {
// 获取当前线程的名字
System.out.println(Thread.currentThread().getName());
for(int i=0;i<30;i++){
System.out.print("A");
}
}
}
三、线程状态
线程的状态分为七种:new(新生状态),runnable(就绪状态),running(运行状态),waiting(等待状态),sleeping(睡眠状态),block(阻塞状态),dead(死亡状态)。
1)new
当使用new来新建一个线程时,一个新的线程就诞生了。但除非在构造函数中调用了start()方法,否则这个新线程将作为一个新对象待在内存中基本什么事情也不做。当对这个线程调用了start()方法,或者这个线程的状态由new改变为runnable后,调度程序就可以把处理器分给这个线程。
线程处于等待状态时,可以通过Thread类的方法来设置线程的各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)etc.
2.runnable、running
把处理器分配给一个处于runnable的线程之后,这个线程的状态就变成了running。如果一个处于running状态的线程能够运行到结束或因某个为捕获的异常而使线程终止时,它的状态就变为dead。否则,这个线程的命运就取决于当前是否还有其他线程等待处理器运行。如果这个线程在当前所有处于runnable状态的线程中具有最高优先级,那么它就会继承执行,除非这个线程被运行程序的平台划分为时间片。当线程被划分为时间片时,一个处于running状态的线程将分配到一个固定间隔的处理器时间。具有相同优先级的线程的时间片调度将导致这些线程被轮流执行。如果正在执行的线程的代码包含了yeild()方法,那么这个处于running状态的线程将停止运行,并把处理器交给其他线程。
可以通过Thread类的isAlive()方法来判断线程是否处于运行状态。当线程处于running状态时,isAlive()返回true,当isAlive()返回false时,可能线程处于等待状态,也可以处于静止状态。
package TreadTest;
public class LifeCycle {
public static void main(String args[]){
Thread3 thread3 = new Thread3();
System.out.println("等待状态[isAlive:" + thread3.isAlive() + "]");
thread3.start();
System.out.println("运行状态[isAlive:" + thread3.isAlive() + "]");
try {
thread3.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程结束[isAlive:" + thread3.isAlive() + "]");
}
}
class Thread3 extends Thread{
public void run() {
int i = 0;
while((++i)<1000)
;
}
}
运行结果:
- 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程在下列情况会结束:
线程run()方法的结尾;
线程抛出一个未捕获异常或Error;
调用interupt()方法中断线程;
调用join()方法等待线程结束;
调用stop()方法直接停止线程。
四、线程优先级
线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得CPU时间时,线程调度系统根据各个线程的优先级来决定CPU分配时间,优先级高的线程有更大的机会获得CPU时间。
每一个线程都有一个优先级。默认情况下优先级通过静态常亮Thread.NORM_PRIORITY定义,该常亮的值为5。每个新线程均继承创建线程的优先级。线程的优先级可以通过setPriority()/getPriority()方法设置和获取,可将线程的优先级设置为MIN_PRIORITY(1)和MAX_PRIORITY(10)之间的值。
当线程睡眠后,优先级就失效了,若想保证优先级继续有效,可考虑使用join()方法解决。
注意:线程优先级高度依赖于操作系统,线程优先级不能保证线程的执行次序,应尽量避免使用线程优先级作为构建任务执行顺序的绝对标准。
五、多线程
一个小例子,通过多线程实现窗口中标签字颜色的不断变化,按钮中的文字不断改变。
package TreadTest;
import java.awt.Color;
import java.awt.Font;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MutiThreadDemo extends JFrame {
JPanel p;
JLabel lbl;
JButton btn;
String s[]={"Good","mornming","everybody","!!!"};
public MutiThreadDemo(){
super("多线程");
p=new JPanel();
lbl=new JLabel("颜色");
//设置字体
lbl.setFont(new Font("宋体",Font.BOLD,20));
btn=new JButton(s[0]);
//设置字体
btn.setFont(new Font("黑体",Font.ITALIC,20));
p.add(lbl);
p.add(btn);
this.add(p);
this.setSize(300,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//创建子线程ColorChange,并启动
new ColorChange().start();
//创建子线程TextChange,并启动
new TextChange().start();
}
//定义子线程,让标签字的颜色不断变化
class ColorChange extends Thread{
public void run(){
while(this.isAlive()){
//随机产生颜色的3个基数0~255
int r = (int)(Math.random()*256);
int g = (int)(Math.random()*256);
int b = (int)(Math.random()*256);
//设置标签的前景颜色,即文字的颜色
lbl.setForeground(new Color(r,g,b));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//定义子线程,让按钮文本不断变化
class TextChange extends Thread{
public void run(){
int i=0;
while(this.isAlive()){
btn.setText(s[i++]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(i==s.length){
i=0;
}
}
}
}
public static void main(String args[]){
MutiThreadDemo f = new MutiThreadDemo();
f.setVisible(true);
}
}