Java多线程机制
//操作系统可以产生多个进程,每个进程也可以产生多个线程
1.线程
//"主线程”是main线程
//线程有4种状态:新建、运行、中断、死亡
(1)新建:
①通过继承Thread类创建:
需要重写Thread类的run()方法;
//优点:可以在子类中增加新的成员变量和方法,使线程具有某种属性和功能。
缺点:Java不支持多继承,Thread类的子类不能再扩展其他的类。
②通过实现Runnable接口创建:
构造方法:Thread(Runnable target)
//优点:对于使用同一目标对象的线程,目标对象的成员变量是这些线程共享的数据单元;
创建目标对象的类在必要时可以是某个特定类的子类。
因此使用Runnable接口比使用Thread的子类更具有灵活性。
中断原因:
①JVM将CPU资源从当前线程切换给其他线程,使本线程让出CPU的使用权处于中断状态。
②线程使用CPU资源期间,执行了sleep(int millsecond)方法,使当前线程进入休眠状态。
③线程使用CPU资源期间,执行了wait()方法,使得当前线程进入等待状态。
④线程使用CPU资源期间,执行某个操作进入阻塞状态,比如执行读/写操作引起阻塞。
死亡(线程释放了实体和线程对象的内存)原因:
①执行完run()方法中的全部语句,结束了run()方法。
②线程被提前强制性地终止,即强制run()方法结束。
目标对象与线程的关系:
①完全解耦:目标对象没有组合线程对象。
②弱耦合:目标对象可以组合线程,即将线程作为自己的成员(在Thread子类或实现Runnable的类中创建线程)。
//在实际问题中,根据实际情况确定目标对象和线程是组合或完全解耦关系,两种关系各有优缺点。
线程的常用方法:
1.start() throws IllegalThreadStateException
启动线程,二次调用start()方法会导致异常;
2.run()
定义线程对象被调度之后所执行的操作,系统自动调用而用户程序不得引用;
3.sleep(int millsecond) throws InterruptedException
参数millsecond是以毫秒为单位的休眠时间,如果线程在休眠时被打断,JVM就抛出异常;
4.isAlive()
判断线程是否存在(非新建和死亡状态);
5.currentThread()
返回当前正在使用CPU资源的线程;
6.interrupt()
"吵醒"休眠的线程,导致休眠的线程发生InterruptedException异常;
7.setPriority(int grade) throws IllegalArgumenException
调整线程的优先级,参数grade参数为1~10之间,若不在该范围则发生异常(注意有些os只识别3个级别:1、5和10);
//实际编程中,不提倡使用线程的优先级来保证算法的正确执行
2.线程同步(用synchronized修饰)
线程同步机制:当一个线程A使用synchronized方法时,其他线程想使用这个synchronized方法时就必须等待,直到线程A使用完该synchronized方法。
同步方法中的方法(都是Object类的final方法):
1.wait()
中断线程的执行,使本线程等待,暂时让出CPU的使用权;
2.notify()
通知处于等待中的某一个线程结束等待;
3.notifyAll()
通知所有由于使用这个同步方法而处于等待的线程结束等待,曾中断的线程从刚才的中断处继续执行这个同步方法;
//不可以在非同步方法中使用上述的三个方法
//遵循"先中断先继续”的原则
3.线程联合
一个线程A在占有CPU资源期间,可以让其他线程调用join()方法和本线程联合,如:B.join();
如果线程A在占有CPU资源期间一旦联合B线程,那么A线程将立刻中断执行,一直等到它联合的B线程执行完毕,A线程再重新排队等待CPU资源,以便恢复执行。
//如果A准备联合的B线程已经结束,那么B.join()不会产生任何效果。
4.GUI线程
当Java程序包含GUI时,JVM在运行程序时会自动启动更多的线程,其中两个重要的线程:AWT-EventQuecue和AWT-Windows
当触发ActionEvent事件时,AWT-EventQuecue线程就立刻等候执行处理事件的代码。
5.计时器线程
javax.swing.Timer类(避免与java.util.Timer类混淆):
构造方法:
Timer(int a)
必须调用addActionListener(ActionListener listener)方法获得监视器(必须是组件类);
Timer(int a,Object b)
参数a的单位是毫秒,确定计时器每隔a毫秒"振铃"一次,参数b是计时器的监视器;
常用方法:
1.setReapeats(boolean b)
只想计时器振铃一次,可以调用此方法,参数b取值false;
2.setInitialDelay(int depay)
设置首次振铃的延时,默认延时为a;
3.start()
启动计时器;
4.stop()
停止计时器,即挂起线程;
5.restart()
重新启动计时器,即恢复线程;
6.守护线程
线程默认是非守护线程(用户线程),一个线程调用void setDaemon(boolean on)方法将自己设置成一个守护线程,如:thread.setDaemon(true);
当所有用户线程结束运行时, 即使守护线程的run方法还未执行完,也立刻结束运行。
//守护线程用来做一些不是很严格的工作