进程和线程概念
- 进程:相对于系统来讲(Win Linux Mac),独立的内存空间,并发性,动态性
- 线程:相对于进程来讲,是进程内部的一个执行单元,内部单一的执行单元,顺序控制流程,共享进程资源
创建线程
- 1.继承Thread类,复写其run()方法,调用start()方法,启动线程
MyThread t = new MyThread ();
t.start();
- 2.实现Runnable接口,实现其run()方法,调用start()方法
MyThread mt = new MyThread ();
Thread t = new Thread(mt);
t.start();
两种创建进程方法的比较
继承Thread类方式的多线程
- 优势:编写简单
- 劣势:无法继承其它父类实现Runnable接口方式的多线程
- 优势:可以继承其它类,多线程可共享同一个Thread对象
- 劣势:编程方式稍微复杂,如果需要访问当前线程,需要调用Thread.currentThread()方法调用start()方法启动线程和直接调用run()方法的区别
- 调用start()方法,会申请新的线程,开辟新的线程空间去执行run()方法中的内容
- 调用run()方法,只是由当前线程去调用run()方法,不会创建新的线程,仅将其当成一个普通方法调用
多线程的优势(重点)
- 多线程使系统空转时间减少,提高CPU利用率
- 进程间不能共享内存,但线程之间共享内存非常容易
- 使用多线程实现多任务并发比多进程的效率高
- Java语言内置多线程功能支持,简化了Java的多线程编程
线程状态
- 新生 创建新的线程对象
- 运行 调用start()方法
- 阻塞
1.sleep(long time) 睡眠time毫秒后,自动唤醒继续运行
2.join()加入 A线程加入到B线程,B线程等待A线程运行完毕再继续向下执行
3.yield()暂停瞬间,然后继续执行 - 死亡 整个线程的run()方法的代码块运行完毕
线程锁
- synchronization(){ }同步代码块 多个线程中存在使用同一把锁的代码块,多个线程会抢这一把锁,抢到的进入同步代码块,剩下的线程全在外面阻塞,阻塞到进入的线程运行完,该线程会释放锁,所有阻塞的线程继续开始抢锁。
- 同步方法 public synchronized void xxx (){ },同步方法的锁,就是锁住的对象就是调用该方法的对象
- 注意 :在拥有同步方法的类中,锁住的是该类的实例化的对象
线程同步的必要性
线程间通信
- wait()和notify()
- 当执行了wait()方法后,本线程终止,将锁释放给正在等待该锁的线程
- 当执行了notify()方法后,线程会唤醒正在等待该锁的对象,但是唤醒后不是立即执行,而是等调用notify()方法的线程执行完同步代码块之后,才开始抢占执行。
Timer
- 定时器的使用
- 首先要实例化Timer对象,
- 然后要重写一个类继承TimerTask类,在复写其中的run()方法
- 然后调用Timer()对象的schedule(TimerTask task, long delay, long period)
- 第一个参数是继承TimerTask类的实例化对象
- 第二个参数是延时多久开始执行其run()方法
- 第三个参数是run()方法循环的间隔时间
public class Test {
public static void main(String[] args) {
Timer timer=new Timer();
timer.schedule(new MyTimerTask(), 2000 ,500);
}
}
public class MyTimerTask extends TimerTask{
private int i=0;
public void run() {
System.out.println("正在计时:"+i++);
}
}
反射详解
- 反射就是可以通过对象实例或者类名来获取Class对象,通过Class对象的各种方法,可以获得该类的各种信息,包括属性,方法,构造器,返回值类型,属性类型等等。
- Class 类的抽象 Person Dog Car
Field 属性的抽象
Method 方法的抽象
Constructor 构造器的抽象
- 可以通过反射的机制,给对象中的属性进行赋值操作
public class ReflectionTest {
public static void main(String[] args) {
Person zhangsan=new Person();
System.out.println(zhangsan.getName());
//此Person中不存在一个可以访问方法去修改名字
//可以使用反射去修改zhangsan这个对象的name
//1得到Person的Class对象
Class<Person> zhangsanClass=(Class<Person>) zhangsan.getClass();
try {
//2得到Person类中名称是name的字段
Field name=zhangsanClass.getDeclaredField("name");
//3取消反射时的java语言访问限制
name.setAccessible(true);
//4使用field修改zhangsan的name的值,因为Person中的name是private的,所以会报错,其他修饰符的话不会报错
name.set(zhangsan, "张三");
//5重新打开反射时的java语言访问限制,以后访问还要检查
name.setAccessible(false);
System.out.println(zhangsan.getName());
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
public class Person {
private String name ;//zheshishenme a
private String sex;
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
设计模式
Dao模式
- 只有属性和set get方法
单例模式
- 在要单例化的类中
- ① 私有化构造器,不允许外部使用new关键字创建对象
- ② 声明一个静态的私有的Person对象
- ③ 提供一个静态的公有的同步方法供外部访问,返回值类型是该类 为什么使用synchroized防止多个线程同时访问这个方法 静态同步方法的锁是指的类本身
- ④ 在该方法中添加限制
public class Person {
private String name;
private String sex;
//创建一个静态的私有的该类对象,声明为静态只能有一个这个对象的实例
private static Person person;
//私有化构造器
private Person() {
}
//创建一个共有的静态的同步方法,声明为同步方法可以限制多个线程同时访问的问题,声明为静态可以通过类名调用
public synchronized static Person newInstance() {
//在其中添加限制,若不为空,就实例化对象,否则仅返回其对象
if (person==null) {
person=new Person();
}
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
public class Test {
//所有输出的内容hashcode是一样的,表示是同一个对象
public static void main(String[] args) {
Person zhangsan=Person.newInstance();
System.out.println(zhangsan);
Person lisi=Person.newInstance();
System.out.println(lisi);
new Thread(new Runnable() {
public void run() {
Person wangwu=Person.newInstance();
System.out.println(wangwu);
}
}).start();
new Thread(new Runnable() {
public void run() {
Person zhaoliu=Person.newInstance();
System.out.println(zhaoliu);
}
}).start();
}
}
简单工厂模式
- 创建一个工厂,使用这个工厂创建这个类的对象
- 创建一个配置文件properties格式,用Properties类从中读取类的信息,根据类的信息用Class的forName(String str),获取累的对象,
- 再用该对象实例化要生成的对象实例
- 要改变生成的对象实例时候,就不用重新编译代码,仅仅修改配置文件的类的信息就可以了
- 另外如果要扩展程序的话,对源代码基本不用改变,直接生成你要添加的类的代码即可
public class Person {
public void speak() {
System.out.println("人类会说话");
}
}
public class Teacher extends Person{
@Override
public void speak() {
System.out.println("老师会讲课");
}
}
public class PersonFactory {
public Person newInstance() {
Person person=null;
Properties pro=new Properties();
try {
//解析配置文件
pro.load(new FileInputStream("config.properties"));
//从配置文件中读取类的信息
String str=pro.getProperty("classname");
//Class类根据类名生成Class的对象
Class name=Class.forName(str);
//由Class对象生成对应类的实例
person=(Person) name.newInstance();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return person;
}
}
public class Test {
public static void main(String[] args) {
PersonFactory factory=new PersonFactory();
Person person=factory.newInstance();
//根据生成对象的不同,输出相应改变
person.speak();
}
}
- 配置文件如下
知识漏洞
static
- 在一个类中,static修饰的类的属性,不管实例化多少个对象,该属性都只有一个,并且对每一个实例化对象共享。
PrintWriter
print()和println()的区别
- print()仅仅是打印输出
- println()打印输出,并且终止当前行的输出