Java多线程是Java开发面试必问的一块,但这块之前在学校里写的比较少,在网上看了很多大神的博客之后,归纳总结了一下。然后也是第一次用这个编辑器,排版格式方面可能有所欠缺。
1.多线程的声明
多线程的声明常用的有两种:(1)继承Thread类;(2)实现Runnable接口
事实上,Thread类也是实现了Runnable接口,我们只需重写run()就可以了。
2.线程的等待与唤醒
// WaitTest.java的源码
class ThreadA extends Thread{
public ThreadA(String name) {
super(name);
}
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName()+" call notify()");
// 唤醒当前的wait线程
notify();
}
}
}
public class WaitTest {
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
synchronized(t1) {
try {
// 启动“线程t1”
System.out.println(Thread.currentThread().getName()+" start t1");
t1.start();
// 主线程等待t1通过notify()唤醒。
System.out.println(Thread.currentThread().getName()+" wait()");
t1.wait();
System.out.println(Thread.currentThread().getName()+" continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
main start t1
main wait()
t1 call notify()
main continue
程序开始于main(),这时cup中跑的线程就是主线程main,“主线程”通过 new ThreadA(“t1”) 新建“线程t1”。随后通过synchronized(t1)获取“t1对象的同步锁”。然后调用t1.start()启动“线程t1”。线程t1执行完了后,通过notify()将主线程重新唤醒。这里需要注意的是,t1.wait()并没有让线程t1等待,而是让主线程main进入了等待状态,这是因为wait()的作用是“引起当前线程的等待状态”,而刚才cup中跑的无疑是主线程main。因此,t1.wait()是让“主线程”等待,而不是“线程t1”!
Java里JDK文档描述如下:
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
In other words, this method behaves exactly as if it simply performs the call wait(0).
The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.
wait(3000)即让当前运行线程进入阻塞状态,在nofity()或nofityall()唤醒后,或者3000ms结束后,该线程会重新进入就绪状态,重新与其他线程竞争。在这里要说的是和sleep()的区别,sleep()并不会释放对象锁,而wait()会。
其他要说的就是,在学习过程中我很奇怪的一点。wait(),notify()等方法都是定义在Object中,而不是Thread中。原来,这些方法都和synchronized()一样,通过对“对象的同步锁”进行操作来控制线程。所以,它们定义在Object中也是理所当然了。
这里在讲一个例子:
##package gg;
public class NotifyAllTest {
private static Object obj = new Object();
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
ThreadA t2 = new ThreadA("t2");
ThreadA t3 = new ThreadA("t3");
t1.start();
t2.start();
t3.start();
try {
System.out.println(Thread.currentThread().getName()+" sleep(3000)");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(obj) {
// 主线程等待唤醒。
System.out.println(Thread.currentThread().getName()+" notifyAll()");
obj.notifyAll();
}
}
static class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run() {
synchronized (obj) {
try {
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " wait");
// 唤醒当前的wait线程
obj.wait();
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
main sleep(3000)
t1 wait
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue
这里main线程先执行,然后new了三个新线程:t1,t2,t3,通过start()让这个三个线程进入就绪态,然后线程main执行完sleep(3000)后休眠3秒,在这三秒里,其他线程都运行了,以"t1"为例,当它运行的时候,它会执行obj.wait()等待其它线程通过notify()或额nofityAll()来唤醒它;相同的道理,"t2"和"t3"也会等待其它线程通过nofity()或nofityAll()来唤醒它们。这里的线程没有优先级区分,属于“竞争上岗”。要提到的一点是,等待main线程执行notify()方法后,执行的线程顺序,由于没有优先级,cpu会将最近wait()的线程先执行。