深入剖析为什么要使用线程协作?

一、为什么要使用线程协作?

(一)没有线程协作时

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方法
显示器线程被唤醒,输出时间

由以上分析可以发现线程间协作运行,可以提高代码的性能,给设计及使用带来很大方便

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值