一、笔记:
Collection:List Set
Iterator:iterator()获取迭代器的对象
Map:HashMap Properties
IO:
字节:InputStream OutputStream
字符:Reader Writer
文件流:FileInputStream FileOutputStream FileReader FileWriter
缓冲流
转换流
打印流
对象流
程序:硬盘中的一个可执行文件,一段静态的代码块。
进程:程序的一次执行过程,运行在内存中的程序
线程:进程中的一条执行路径。
多线程:进程中的多条执行路径。
Thread(线程):Java提供的创建线程的类
run():放入需要执行的代码
start():用来启动线程,默认调用run()。
继承Thread
1.将类声明为 Thread 的子类
2.重写 Thread 类的 run 方法
3.创建子类对象
4.使用start()启动线程
实现Runnable(必须掌握)
1.将类实现Runnable
2.实现run方法
3.创建Thread对象,将子实现类对象作为参数传递到Thread对象中
4.启动线程
多线程:
基本概念
•程序 - 数据结构 + 算法。指的是硬盘中的一个可执行文件
•进程 - 主要指运行在内存中的程序
目前主流的操作系统都支持多进程,是为了让操作系统能够同时执行多个任务,但进程是 重量级的,新建进程对系统资源的消耗比较大,因此进程的数量是有限的。
•线程 - 是进程内部的程序流,也就是说操作系统中支持多进程,而每个进程的内部又可以支持多线程,并且线程是轻量级的,新建线程会共享所在进程的资源,因此对资源消耗比较小。
以后的主流开发都采用多线程机制,而操作系统中主要采用时间片轮转法来保证多任务的同时执行,该策略叫做并发机制,也就是宏观并行,微观串行的机制。
线程的创建(重中之重)
Thread类创建线程的方式
•Thread类主要用于描述线程,Java虚拟机允许Java程序启动多个线程并同时执行
•创建线程的方式:
• a.自定义类继承Thread类并重写run()方法,然后创建自定义类的对象调用start()方法
• b.自定义类实现Runnable接口并重写run()方法,然后创建自定义类的对象作为创建Thread类对象的实参,最后使用Thread类的对象调用start()方法。
创建线程的有关方法
Thread() - 无参的方式构造对象。
Thread(String name) - 使用参数指定的名称来构造对象。
Thread(Runnable target) - 根据参数指定的引用来构造对象。
- 其中Runnable是个接口,实参的传递方式有两种:
a.自定义类实现Runnable接口,构造自定义类的对象作为实参传递。
b.使用匿名内部类直接构造接口类型的引用作为实参传递。
Thread(Runnable target, String name)
- 使用参数指定的引用和名称共同构造对象
线程创建的执行原理
执行main()方法的线程叫做主线程,执行run()方法的线程叫做子/新线程。
对于start()方法之前的代码来说,由主线程执行一次,当start()方法调用成功后,则线程的个数瞬间由1个变成了2个,其中新创建的子线程去执行run()方法,主线程继续执行后续代码,两个线程同时执行互不影响。
当run()方法结束时则子线程结束,当main()方法结束时则主线程结束,两个线程执行的先后次序没有明确的规定,由系统调度算法决定。
注意:
创建线程两种方式里面继承相对简单容易理解,但是Java语言支持单继承,若一个类继承Thread类后则无法继承其他类,因此推荐使用第二种方式来创建线程,从而提高代码的可维护性和可扩展性。
线程的编号和名称(熟悉)
• long getId() - 用于获取调用对象的线程编号。
• String getName() - 用于获取调用对象的线程名称。
• void setName(String name) - 用于设置线程名称为参数指定数值。
• static Thread currentThread() - 用于返回当前正在执行线程对象的引用。
线程的主要状态(熟悉)(生命周期)
新建状态 - 当使用new关键字创建线程对象之后进入的状态。
- 此时该线程并没有开始执行。
就绪状态 - 当线程对象调用start()方法之后进入的状态。
- 此时该线程仍然没有开始执行。
运行状态 - 当线程对象被线程调度器调度之后进入的状态。
- 此时该线程开始执行。
- 当线程对象的时间片执行完毕但任务没有完成时回到就绪状态。
消亡状态 - 当线程对象的时间片执行完毕并且任务已经完成后进入的状态。
阻塞状态 - 当线程执行过程中发生阻塞事件后进入的状态,如:sleep()方法。
- 当阻塞解除后进入就绪状态。
线程的常用方法
static void sleep(long millis)
- 用于使得当前正在执行的线程进入休眠状态,休眠参数指定的毫秒(重点)。
int getPriority() - 用于获取调用对象的优先级并返回。
线程中的优先级:1 5 10 默认优先级为:5
Thread类中提供了三个字段表示级别:MAX_PRIORITY, MIN_PRIORITY ,NORM_PRIORITY
void setPriority(int newPriority) - 用于设置调用对象的优先级。
- 优先级越高则表示优先获取CPU时间片的机会越大,但不保证该线程一定先执行。
void join() - 用于等待调用对象所表示的线程终止(重点)。
void join(long millis) - 用于等待调用对象所表示线程终止的最长时间为参数指定毫秒
boolean isDaemon() - 用于判断调用对象所表示的线程是否为守护线程。
void setDaemon(boolean on) - 用于设置调用对象所表示线程为守护线程。
- 该方法的调用必须在线程调用start()方法之前。
- 当所有非守护线程结束时,则守护线程随之结束。
线程的同步机制(重点)
基本概念
当多个线程同时访问同一种共享资源时,可能会造成数据的不一致或覆盖等问题,此时就需要对线程之间进行协调和通信,该方式就叫做线程的同步机制。
案例:取钱
解决方案
由程序结果可知:当两个线程同时进行取款时,导致最终的账户余额不合理。
原因:线程一还没有来得及将取款后的余额写入数据库,但线程二已经开始执行。
解决方案:将线程的并发操作改为线程的串行操作即可。
引发问题:该方式会造成线程执行的效率降低,因此以后的开发中能不用则不用。
实现方式 synchronized
在Java语言中可以借助synchronized关键字实现同步锁/对象锁机制,来保证线程执行的原子性,具体方式如下:
(1)使用同步语句块的机制来实现
synchronized(对象的引用){
编写所有需要锁定的代码块;
}
(2)使用synchronized关键字修饰整个方法,表示锁定整个方法的方法体。
该方式等价于 synchronized(this){ 方法体; }。
实现原理(尽量理解)
•当多个线程启动之后同时去抢占共享资源,若其中一个线程抢到了共享资源,则其他线程进入阻塞/等待状态,直到该线程执行完毕所有锁定的代码后会自动释放对象锁,此时等待的线程又可以抢占共享资源,抢到对象锁的代码执行锁定的代码,抢不到的线程继续等待。
死锁(了解)
•同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。
•
经验:
• 在以后的开发中切记不要使用同步语句块的嵌套结构!!!
线程通信--等待唤醒机制
•概念——线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。
•wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。
•notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。
•notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。
•其实,所谓唤醒的意思就是让 线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。
二、巩固
package com.qfedu.zhuguangyi.thread;
public class Singleton {
private Singleton() {}
private static Singleton sin = null;
private static Object o = new Object();
public static Singleton getSingleton() {
synchronized (o) {
if (sin == null) {
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
sin = new Singleton();
}
}
return sin;
}
}
package com.qfedu.zhuguangyi.thread;
public class Test extends Thread{
public void run() {
Singleton singleton = Singleton.getSingleton();
System.out.println(singleton);
}
}
package com.qfedu.zhuguangyi.thread;
public class TestSin {
public static void main(String[] args) {
Test t = new Test();
Test t1 = new Test();
t.start();
t1.start();
}
}