一、多线程实例(run, start, wait, notify, notifyAll, sleep, yield, join)
(一)run和start()方法
1、把需要处理的代码放到run()方法中,start()方法启动线程将自动调用run()方法,这个由java的内存机制规定的。并且run()方法必需是public访问权限,返回值类型为void。
public class ThreadTest extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.print(" " + i);
}
}
public static void main(String[] args) {
new ThreadTest().start();
new ThreadTest().start();
}
}
2、这是个简单的多线程程序。run() 和start() 是大家都很熟悉的两个方法。把希望并行处理的代码都放在run() 中;stat() 用于自动调用run(),这是JAVA的内在机制规定的。并且run() 的访问控制符必须是public,返回值必须是void(这种说法不准确,run() 没有返回值),run()不带参数。
(二)wait()、notify()、notifyAll()方法
1、这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用。synchronized关键字用于保护共享数据,阻止其他线程对共享数据的存取,但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。
(1)wait() 方法:使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。
(2)notify()方法:当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则 notify()不起作用。
(3)notifyAll()方法:从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
public class ThreadTest implements Runnable {
public static int shareVar = 0;
public synchronized void run() {
if (shareVar == 0) {
for (int i = 0; i < 10; i++) {
shareVar++;
if (shareVar == 5) {
try {
this.wait();
} catch(Exception e) {}
}
}
}
if (shareVar != 0) {
System.out.print(Thread.currentThread().getName());
System.out.println(" shareVar = " + shareVar);
this.notify();
}
}
public static void main(String[] args) {
Runnable r = new ThreadTest();
Thread t1 = new Thread(r, "t1");
10 Thread t2 = new Thread(r, "t2");
t1.start();
t2.start();
}
}
运行结果:
t2 shareVar = 5
t1 shareVar = 10
t1线程最先执行。由于初始状态下shareVar为0,t1将使shareVar连续加1,当shareVar的值为5时,t1调用wait() 方法,t1将处于休息状态,同时释放锁标志。
这时t2得到了锁标志开始执行,shareVar的值已经变为5,所以t2直接输出shareVar的值,然后再调用notify() 方法唤醒t1。
t1接着上次休息前的进度继续执行,把shareVar的值一直加到10,由于此刻shareVar的值不为0,所以t1将输出此刻shareVar的值,然后再调用notify() 方法,由于此刻已经没有等待锁标志的线程,所以此调用语句不起任何作用。
(三)sleep()方法
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如:有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有 Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
public class ThreadTest implements Runnable {
public void run() {
for (int k = 0; k < 5; k++) {
if (k == 2) {
try {
Thread.currentThread().sleep(5000);
} catch(Exception e) {}
}
System.out.println(Thread.currentThread().getName() + " : " + k);
}
}
public static void main(String[] args) {
Runnable r = new ThreadTest();
Thread t1 = new Thread(r, "t1_name");
Thread t2 = new Thread(r, "t2_name");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
t1被设置了最高的优先级,t2被设置了最低的优先级。t1不执行完,t2就没有机会执行。但由于t1在执行的中途休息了5秒中,这使得t2就有机会执行了。
(四)yield()方法
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
public class ThreadTest implements Runnable {
public void run() {
8
for (int k = 0; k < 10; k++) {
if (k == 5 && Thread.currentThread().getName().equals("t1")) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " : " + k);
}
}
public static void main(String[] args) {
Runnable r = new ThreadTest();
Thread t1 = new Thread(r, "t1");
Thread t2 = new Thread(r, "t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
输出结果:
t1 : 0
t1 : 1
t1 : 2
t1 : 3
t1 : 4
t1 : 5
t1 : 6
t1 : 7
t1 : 8
t1 : 9
t2 : 0
t2 : 1
t2 : 2
t2 : 3
t2 : 4
t2 : 5
t2 : 6
t2 : 7
t2 : 8
t2 : 9
(五)join()方法
join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
public class JoinTest{
public static void main(String[] args){
try {
ThreadA t1 = new ThreadA("t1"); // 新建“线程t1”
t1.start(); // 启动“线程t1”
t1.join(); // 将“线程t1”加入到“主线程main”中,并且“主线程main()会等待它的完成”
System.out.printf("%s finish\n", Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run(){
System.out.printf("%s start\n", this.getName());
// 延时操作
for(int i=0; i <1000000; i++)
;
System.out.printf("%s finish\n", this.getName());
}
}
}
运行结果:
(01) 在“主线程main”中通过 new ThreadA("t1") 新建“线程t1”。 接着,通过 t1.start() 启动“线程t1”,并执行t1.join()。
(02) 执行t1.join()之后,“主线程main”会进入“阻塞状态”等待t1运行结束。“子线程t1”结束之后,会唤醒“主线程main”,“主线程”重新获取cpu执行权,继续运行。
t1 start
t1 finish
main finish