Java基础回顾(三)

29)单元测试:对软件中最小可测试单元进行检查和验证。通常是函数/方法。(已知代码结构进行的测试,是白盒测试,一般由程序员自己完成)。

集成测试:将多个单元相互作用,形成一个整体,对整体协调性进行测试。一般从最小单元开始,持续推进到单元之间的接口直到集成为一个完整的软件为止。

自动测试:用程序批量、反复的进行测试,并可自动检查程序是否满足预定的要求。

手动测试:手动执行、手动输入,手动检查结果是否满足预定的要求。

回归测试:修改旧代码以后,重新测试以确认修改没有引入新的错误或导致其他代码产生错误。

30)JUnit是一个Java语言的单元测试框架。测试框架比main函数测试策略更好,显示直观方便提高测试效率。JUnit可以单独存在,发布时不需要单独删除。每一个测试方法的头部加@Test,这样JUnit会自动执行这些测试方法。

import static 导入一个类中的所有静态方法。

31) 源文件建议采用UTF-8编码,和外界(文本文件)的输入输出尽量采用UTF-8编码。

Java的程序内部采用UTF-16编码储存所有字符(不受程序员控制)

32)多进程的优点:同时运行多个任务,当程序因IO堵塞时,可以释放CPU,让CPU充分工作。系统有多个CPU时,可以为多个程序同时服务。缺点:笨重,不易管理和切换。

33)多线程:一个程序可以包含多个子任务,可以串行和并行。每个子任务可以称为一个线程。如果一个子任务堵塞,程序可以将CPU调度另外一个子任务进行工作。CPU还是保留在本程序中,但本程序所获得CPU时间和利用率提高了。

34)多进程 对 多线程

  1. 线程共享数据
  2. 线程通讯更高效
  3. 线程更轻量级,更容易切换
  4. 多个线程更容易管理

35)多线程创建:线程继承Thread类,实现run方法。 线程实现Runnable接口,实现run方法。

public class Thread1 extends Thread{
  public void run(){
    System.out.println("Thread1");  
  }
}

public class Thread2 implements Runnable{
  public void run(){
    System.out.println("Thread2");
  }
}

启动时,Start方法会自动以新进程调用run方法,若直接调用run方法,会变成串行执行。

Start只能一次。多线程的启动先后顺序是随机的。

线程无需关闭,run方法结束后,线程自动关闭。main函数可能早于新线程结束,整个程序并不终止。而整个程序的终止是等所有线程都终止。(包括main函数线程)

Thread vs Runnable

  • 1.Thread占据了父类的名额,不如Runable方便
  • 2.Thread类实现Runable,继承了Thread类,本质上是实现Runable接口
  • 3.Runable启动时需要Thread类的支持{Thread t = new Thread(tt); t.start();}(tt为实现了Runnable的对象)
  • 4.Runable更容易实现多线程中资源共享

多线程信息共享:通过共享变量。1.static变量 2.同一个Runnable类的成员变量。但是只使用共享变量进行工作会出现问题 1.各线程的工作缓存副本数据不一致 2.关键步骤缺乏加锁限制。

比如,启动四个线程出售100张门票。由于线程并非直接从内存中进行数据读写,而是先加载入工作缓存中再进行操作。,所以i++,i–不是原子性操作,中间有着许多步骤。

例:读取主存i(正本)到工作缓存(副本)中,每个CPU执行(副本)i-1操作,CPU将结果写入到缓存(副本)中,数据再从工作缓存(副本)刷到主存(正本)中。这个问题同数据库中的并发问题相同。

变量副本问题采用volatile关键字修饰变量。 保证不同线程对共享变量操作时的可见性。

public class ThreadDemo2
{
	public static void main(String args[]) throws Exception 
	{
		TestThread2 t = new TestThread2();
		t.start();
		Thread.sleep(2000);
		t.flag = false; 
		System.out.println("main thread is exiting");
	}
}

class TestThread2 extends Thread
{
	boolean flag = true;//子线程不会停止
	//volatile boolean flag = true;//用volatile修饰的变量可以及时在各线程里面通知
	public void run() 
	{
		int i=0;
		while(flag)
		{
			i++;			
		}
		System.out.println("test thread3 is exiting");
	}	
} 

子线程内flag为boolean型时,主函数的t.flag = true命令不会被接收到。因为内存和工作缓存中的flag已经不同了,子线程工作缓存中的flag还是true的,对内存flag的修改无反应。

加锁限制:

  • 1.互斥:某一线程运行代码段时,其他线程不能同时运行此代码段。
  • 2.同步:多个线程的运行,必须按照某一种规定的先后顺序来运行。
  • 3.互斥是同步的一种特例。
  • Synchronized代码块/函数,只能一个线程进入。
  • Synchronized加大性能负担,但是使用简便。
public class ThreadDemo3 {
	public static void main(String[] args) {
		TestThread3 t = new TestThread3();
		new Thread(t, "Thread-0").start();
		new Thread(t, "Thread-1").start();
		new Thread(t, "Thread-2").start();
		new Thread(t, "Thread-3").start();
	}
}

class TestThread3 implements Runnable {
	private volatile int tickets = 100; //多个线程在共享的
	String str = new String("");

	public void run() {
		while (true) {
			sale();
			try {
				Thread.sleep(100);
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
			if (tickets <= 0) {
				break;
			}
		}

	}

	public synchronized void sale() { //同步函数
		if (tickets > 0) {
			System.out.println(Thread.currentThread().getName() + " is saling ticket " + tickets--);
		}
	}
}