- 什么是线程协作
有些情况下,多个线程合作完成一件事情的几个步骤,此时线程之间实现了协作。如一个工作需要若干个步骤,各个步骤都比较耗时,不能因为他们的运行,影响程序的运行效果,最好的方法就是将各个步骤用线程实现。但是,由于线程随时都有可能抢占cpu,可能在前面一个步骤没有完成时,后面的步骤就已经运行,该安全隐患造系统得不到正确结果。 - 一个案例
线程1负责完成一个复杂运算(比较耗时),线程2负责得到结果,并将结果进行下一步处理。
比如,某个科学计算系统中,线程1负责计算1---100000各个数字的和,线程2负责得到这个结果并且写入数据库。用传统方法编写这段代码如下:
public class ThreadCooperateTest
{
private long sum =0;
class CalThread extends Thread
{
public void run()
{
for(int i =1;i<100000;i++)
{
sum +=i;
}
}
}
class SaveThread extends Thread
{
public void run()
{
System.out.println("写入数据库: sum ="+sum);
}
}
public void work()
{
CalThread ct = new CalThread();
SaveThread st = new SaveThread();
ct.start();
st.start();
}
public static void main(String[] args)
{
new ThreadCooperateTest().work();
}
}
public class ThreadCooperateTest
{
private long sum =0;
class CalThread extends Thread
{
public void run()
{
for(int i =1;i<100000;i++)
{
sum +=i;
}
}
}
class SaveThread extends Thread
{
public void run()
{
System.out.println("写入数据库: sum ="+sum);
}
}
public void work()
{
CalThread ct = new CalThread();
SaveThread st = new SaveThread();
ct.start();
st.start();
}
public static void main(String[] args)
{
new ThreadCooperateTest().work();
}
}
运行结果,发现不正确,而且每次输入的结果不一样。这是因为,当线程ct运行后,线程st运行,此时线程st随时可能抢占cpu,而不一定要等线程ct运行完毕。此时,在求和还没有开始做或者只完成了一部分时,就打印出来sum,所以结果错误。
解决这个错误,要用到join()方法,代码如下:
public class ThreadCooperateTest
{
private long sum =0;
class CalThread extends Thread
{
public void run()
{
for(int i =1;i<100000;i++)
{
sum +=i;
}
}
}
class SaveThread extends Thread
{
public void run()
{
System.out.println("写入数据库: sum ="+sum);
}
}
public void work()
{
CalThread ct = new CalThread();
SaveThread st = new SaveThread();
ct.start();
/*
ThreadCooperateTest.java:27: 未报告的异常 java.lang.InterruptedException;必须对
其进行捕捉或声明以便抛出
ct.join();
^
1 错误
ct.join();必须要catch其异常
*/
try
{
ct.join();
}catch(Exception e)
{}
st.start();
}
public static void main(String[] args)
{
new ThreadCooperateTest().work();
}
}
public class ThreadCooperateTest
{
private long sum =0;
class CalThread extends Thread
{
public void run()
{
for(int i =1;i<100000;i++)
{
sum +=i;
}
}
}
class SaveThread extends Thread
{
public void run()
{
System.out.println("写入数据库: sum ="+sum);
}
}
public void work()
{
CalThread ct = new CalThread();
SaveThread st = new SaveThread();
ct.start();
/*
ThreadCooperateTest.java:27: 未报告的异常 java.lang.InterruptedException;必须对
其进行捕捉或声明以便抛出
ct.join();
^
1 错误
ct.join();必须要catch其异常
*/
try
{
ct.join();
}catch(Exception e)
{}
st.start();
}
public static void main(String[] args)
{
new ThreadCooperateTest().work();
}
}
解决方法:
在运行线程ct时,命令另一个线程st等待线程ct运行完毕,才能抢占cpu进行运行。在java语言中,只需要调用线程ct的join()方法,就能够让系统等起运行完毕才能运行下面的代码。这次输出的结果正确了。
注意:
实际上,该程序相当于摒弃了“线程就是为了程序看起来同时做好几件事情”的思想,将并发程序又变成了顺序的,如果线程ct没有运行完毕的话,程序会在ct.join()处堵塞。如果work耗时较长,程序将一直等待。解决方式是,可以将work函数放在另一个线程中,既不会堵塞主程序,又能够保证数据安全性。
练习:将例子中的work函数写在另外一个线程内,在主函数中调用
代码如下:
public class ThreadCooperateTest
{
private long sum =0;
class CalThread extends Thread
{
public void run()
{
for(int i =1;i<1000000;i++)
{
sum +=i;
}
}
}
class SaveThread extends Thread
{
public void run()
{
System.out.println("写入数据库: sum ="+sum);
}
}
public void work()
{
CalThread ct = new CalThread();
SaveThread st = new SaveThread();
ct.start();
/*
ThreadCooperateTest.java:27: 未报告的异常 java.lang.InterruptedException;必须对
其进行捕捉或声明以便抛出
ct.join();
^
1 错误
ct.join();必须要catch其异常
*/
try
{
ct.join();
}catch(Exception e)
{}
st.start();
}
public static void main(String[] args)
{
Thread t = new Thread()
{
public void run()
{
new ThreadCooperateTest().work();
}
};
t.start();
}
}
public class ThreadCooperateTest
{
private long sum =0;
class CalThread extends Thread
{
public void run()
{
for(int i =1;i<1000000;i++)
{
sum +=i;
}
}
}
class SaveThread extends Thread
{
public void run()
{
System.out.println("写入数据库: sum ="+sum);
}
}
public void work()
{
CalThread ct = new CalThread();
SaveThread st = new SaveThread();
ct.start();
/*
ThreadCooperateTest.java:27: 未报告的异常 java.lang.InterruptedException;必须对
其进行捕捉或声明以便抛出
ct.join();
^
1 错误
ct.join();必须要catch其异常
*/
try
{
ct.join();
}catch(Exception e)
{}
st.start();
}
public static void main(String[] args)
{
Thread t = new Thread()
{
public void run()
{
new ThreadCooperateTest().work();
}
};
t.start();
}
}