一、为什么要使用线程协作?
(一)没有线程协作时
1.源代码
package multithreading;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ElectronicWatch {
String currentTime;
public static void main(String[] args) {
new ElectronicWatch().new DisplayThread().start();//由于下面两个类是内部类要用这种方法新建线程
//显示器线程处于就绪状态后,由于只有显示器线程处于就绪状态所以它直接获得了CPU的资源进入运行状态
}
/**
* 该线程负责显示时间
*/
class DisplayThread extends Thread{
@Override
public void run() {
new TimeThread ().start();
System.out.println(currentTime);
}
}
/**
* 该线程负责获取时间
*/
class TimeThread extends Thread{
@Override
public void run() {
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
currentTime = sdf.format(new Date());
}
}
}
2.代码分析
上述代码执行结果是“null”
因为显示器线程进入运行状态后,调用run方法,使得时间线程进入就绪状态
①如果此时显示器线程比较强势仍然持有CPU的资源,执行System.out.println语句,全局变量currentTime尚未赋值,故输出null
②如果此时时间线程比较强势获得了CPU的资源,调用run方法,阻塞3s,这3s内显示器线程有充分的时间获得CPU的资源,执行System.out.println语句,全局变量currentTime尚未赋值,故输出null
(二)改进
1.源代码
package multithreading;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ElectronicWatch {
String currentTime;
public static void main(String[] args) {
new ElectronicWatch().new DisplayThread().start();
}
/**
* 该线程负责显示时间
*/
class DisplayThread extends Thread{
@Override
public void run() {
TimeThread timeThread = new TimeThread();
timeThread.start();
try {
sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentTime);
}
}
/**
* 该线程负责获取时间
*/
class TimeThread extends Thread{
@Override
public void run() {
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
currentTime = sdf.format(new Date());
}
}
}
2.代码分析
上述代码执行结果能输出时间
因为显示器线程进入运行状态后,调用run方法,使得时间线程进入就绪状态
①如果此时显示器线程比较强势仍然持有CPU的资源,阻塞5s,这5s内时间线程有充分的时间获得CPU的资源,进入运行状态,阻塞3s并给全局变量currentTime赋值
②如果此时时间线程比较强势获得了CPU的资源,调用run方法,阻塞3s,这3s内显示器线程有充分的时间获得CPU的资源,阻塞5s,时间线程有充分的时间再次获得CPU的资源,给全局变量currentTime赋值
故输出无论如何都能输出时间
注意:该方法不可取,因为性能不高
(三)性能优化
1.源代码
import java.text.SimpleDateFormat;
import java.util.Date;
public class ElectronicWatch {
String currentTime;
public static void main(String[] args) {
DisplayThread displayThread = new ElectronicWatch().new DisplayThread();//显示器
displayThread.setPriority(10);
displayThread.start();
}
/**
* 该线程负责显示时间
*/
class DisplayThread extends Thread{
@Override
public void run() {
TimeThread timeThread = new TimeThread();
timeThread.setPriority(1);
timeThread.start();//时间线程就绪
try {
timeThread.join();//执行该方法的线程阻塞,调用该方法的线程进入就绪状态
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentTime);
}
}
/**
* 该线程负责获取时间
*/
class TimeThread extends Thread{
@Override
public void run() {
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
currentTime = sdf.format(new Date());
}
}
}
2.代码分析
上述代码执行结果能输出时间
因为显示器线程进入运行状态后,调用run方法,使得时间线程进入就绪状态
①由于设置显示器线程的优先级高于时间线程,使用此时显示器线程比较强势仍然持有CPU的资源,执行join语句进入阻塞状态。
时间线程调用join语句进入就绪状态,这3s时间内由于时间线程还未执行完,显示器线程仍处于阻塞状态
时间线程执行语句并给全局变量currentTime赋值
时间线程死亡,显示器线程获得CPU资源输出时间
②如果设置时间线程的优先级高于显示器线程,时间线程比较强势获得了CPU的资源,调用run方法,阻塞3s
这3s内显示器线程有充分的时间获得CPU的资源,执行join语句进入阻塞状态。
时间线程调用join语句进入就绪状态 ,执行语句给全局变量currentTime赋值
故输出无论如何都能输出时间
(四)深入优化
1.源代码
import java.text.SimpleDateFormat;
import java.util.Date;
public class ElectronicWatch {
String currentTime;
DisplayThread displayThread = new DisplayThread();//定义为全局变量作为共享数据
public static void main(String[] args) {
new ElectronicWatch().displayThread.start();//显示器线程就绪
}
/**
* 该线程负责显示时间
*/
class DisplayThread extends Thread{
@Override
public void run() {
synchronized (this) {
new TimeThread().start();
try {
this.wait();//这里的this指的是调用run方法的对象,即显示器线程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentTime);//输出结果为什么一定不为null
}
}
}
/**
* 该线程负责获取时间
*/
class TimeThread extends Thread{
@Override
public void run() {
synchronized (displayThread) {//由于displayThread是全局变量,故这里的displayThread和显示器获得的this是同一个对象
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
currentTime = sdf.format(new Date());
displayThread.notify();
}
}
}
}
2.代码分析
显示器线程就绪后,主线程执行完代码死亡
只有显示器线程存活,进入运行状态,获得对象锁
时间线程就绪,显示器线程调用wait方法后失去锁进入等待状态
时间线程获得对象锁,执行完代码给currentTime赋值后,displayThread对象执行notify方法
显示器线程被唤醒,输出时间
由以上分析可以发现线程间协作运行,可以提高代码的性能,给设计及使用带来很大方便