今日内容
零、 复习昨日
一、作业
二、进程与线程
三、创建线程
四、线程的API
一、复习
IO流的分类
- 方向: 输入,输出
- 类型: 字节(XxxStream),字符(XxxReader,XxxWriter)
- 字节输入流类名: FileInputStream
- 字节输出流类名: FileOutputStream
- 字符输入流类名: FileReader
- 字符输出流类名: FileWriter
利用try-catch-finally代码写法是重点
ArrayList和LinkedList的异同点
- ArrayList 底层是数组,容量10,装满扩容1.5倍
- LinkedList 底层是链表
- 相同点: 都是存储多个元素,有序,且允许重复元素
- 不同点: 底层实现原理不一样,AL查询更新快 LL插入删除较快
解析字符串日期为日期对象的方法签名
Date date = sdf.parse(“2020-01-01”);
Date parse(String s)解析字符串数字为int数字的方法签名
static int parseInt(String s)int n = Integer.parseInt(“1”);
String,Date,try-catch-finally
ArrayList,HashMap
认真,慢一点,写一遍(用法,解释,代码)到本上
二、进程与线程[了解]
进程:
一个
进程就是一个应用程序,进程包含线程
一个进程至少包含一个线程,大部分都是有多条线程在执行任务(多线程),每个独立运行的程序都对应一个进程。进程是资源分配的最小单位
,占用独立的内存空间和系统资源
- qq,微信,迅雷
线程:
线程是进程内部的一个执行任务
进程内多个线程是共享进程资源,
线程是资源调度的最小单位(CPU调度和分派的基本单位)
- qq在聊天,视频,传输文件
- 迅雷同时下载多个资源
Java程序是否是多线程的吗?
答: 是! main线程,gc垃圾回收线程
为什么需要多线程?
- 并行处理任务,提高效率
Java程序本身,是否多线程?
- 是,至少有一个线程是"main"线程
- 还有一个线程是没有直接看到,但是一直在"背后"运行,垃圾回收线程(GC)
三、创建线程[重点]
Thread
创建线程的方式有很多
继承Thread
实现Runnable接口
- 使用Callable 和FutureTask来完成
- 使用线程池获得线程
3.1 继承Thread
步骤
- 自定义类
- 继承Thread
- 重写
run
方法- 创建子类对象
调用start方法启动线程
[特别强调,开启新线程的方法是start
]
start方法内部执行时会调用run方法执行
public class MyThread extends Thread{
// 重写方法,方法内部,就是该线程执行的任务
@Override
public void run() {
for (int i = 1; i < 101; i++) {
System.out.println("MyThread --> "+i );
}
}
}
public class TestThread{
public static void main(String[] args) {
// 创建一个线程
MyThread myThread = new MyThread( );
// 启动线程,不是调用run()方法!!!!
// 调用start方法开启新线程
myThread.start();
// ==============main线程执行====================
for (int i = 1; i < 101; i++) {
System.out.println("main --> " + i );
}
// 自定义线程和主线程同时执行,并且出现资源争抢情况
}
}
3.2 实现Runnable接口
步骤
- 自定义类
- 实现Runnable接口
- 重写run方法
- 创建子实现类对象
把子实现类对象当构造方法的方法参数来创建Thread对象
- 由thread调用start方法开启线程
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 1; i < 101; i++) {
System.out.println("MyRunnable --> " + i);
}
}
}
public static void main(String[] args) {
// 4 创建实现类对象
MyRunnable myRunnable = new MyRunnable( );
// 5 把对象当构造方法参数,创建Thread对象
Thread thread = new Thread(myRunnable);
// 6 开启线程
thread.start();
}
匿名内部类的形式创建线程
// ============= 匿名内部类完成实现Runnable接口 ============
public class Demo4Annoy {
public static void main(String[] args) {
Runnable runnable = new Runnable( ) {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("匿名内部类 --> " + i);
}
}
};
Thread thread = new Thread(runnable);
thread.start();
// new Thread( new Runnable() {
// @Override
// public void run() {
// for (int i = 0; i < 100; i++) {
// System.out.println("匿名内部类 --> " + i );
// }
// }
// }).start();
for (int i = 0; i < 100; i++) {
System.out.println("main --> " + i);
}
}
}
3.3 区别
继承Thread开启线程和实现Runnable接口开启线程有什么区别?
- 一个继承,一个实现
- 继承Thread后,直接创建对象即可调用start开启线程
- 实现Runnable接口的子类,还需要再创建Thread类对象才可以调用strat开启线程
从使用便捷度来说,继承Thread开启线程会方便一点…因为创建完线程对象可以直接调用start开启线程
继承Thread类就限制了该类不能再继承别的类,因为类只能单继承,而实现接口的同时可以继承别的类,并且还允许多继承,
所以推荐使用接口来实现多线程.
3.4 其他创建线程方式
package com.qf.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
public class Callable01 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// FutureTask对象用于接收线程返回值, FutureTask 实现了RunnableFuture接口
// public class FutureTask<V> implements RunnableFuture<V> {}
// RunnableFuture接口继承了Runnable和Future接口,后面用到的get方法来自Future接口
// public interface RunnableFuture<V> extends Runnable, Future<V>{}
FutureTask<String> futureTask = new FutureTask<String>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("-----------------");
// 该语句阻塞程序的执行,等待线程执行完毕,获取线程返回的数据
String result = futureTask.get();
System.out.println(result);
}
}
// 实现Callable接口的类
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
return "haha";
}
}
四、Thread的API[熟悉]
void start() 开启线程
,会自动run方法执行线程任务void run() 执行线程的方法
- run() 方法是start开启线程后,JVM自动调用
- void setName(String name) 给线程设置名字
- 也可以通过构造方法,在创建线程时指定线程名
- String getName() 获得线程的名字
static Thread currentThread()
返回当前正在执行的线程对象- join() 加入线程,等待该线程终止
- join(int milles) 加入线程,最大等待直到毫秒数
- void setDaemon(boolean on) 设置守护线程
static void sleep(long milles) 线程休眠
- 会让当前线程暂停执行,
让出系统资源
,让其他线程执行- 时间到,当前会继续和其他争抢资源执行
- void stop() 结束当前线程,线程死亡(已过式)
- 被废弃的原因是因为这个中断线程太直接太暴力…
线程名字
- String getName()
- void setName()
- Thread(String name)
package com.qf.thread_api;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc 关于线程名字操作
*/
public class Demo1 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
// 给线程设置名字
this.setName("驴车");
// 创建线程,默认会分配名字,安装thread-x来命名
System.out.println("开启新线程,线程名: " + getName());
}
}.start();
new Thread(){
@Override
public void run() {
this.setName("火车");
// 创建线程,默认会分配名字,安装thread-x来命名
System.out.println("开启新线程,线程名: " + getName());
}
}.start();
new Thread(){
@Override
public void run() {
this.setName("飞机");
// 创建线程,默认会分配名字,安装thread-x来命名
System.out.println("开启新线程,线程名: " + getName());
}
}.start();
Thread thread = new Thread(){
@Override
public void run() {
}
};
System.out.println(thread.getName( ));
thread.setName("火箭");
System.out.println(thread.getName( ));
/**
* 还可以通过构造方法,在创建线程时指定线程名
*/
Thread thread5 = new Thread("子弹头"){
@Override
public void run() {
}
};
System.out.println("线程5-->" + thread5.getName( ));
}
}
获得当前线程对象
- static Thread currentThread()
public static void main(String[] args) {
new Thread("火车"){
@Override
public void run() {
Thread t = Thread.currentThread( );
System.out.println(t.getName()+",在执行..." );
}
}.start();
/**
* 获得当前正在执行的线程对象
*/
Thread thread = Thread.currentThread( );
System.out.println("当前正在执行的线程名字是: "+thread.getName( ));
}
线程加入
- join()
- join(long mill)
public static void main(String[] args) {
Thread t1 = new Thread("线程1"){
@Override
public void run() {
for (int i = 1; i < 1001; i++) {
System.out.println(getName()+"-->"+i );
}
}
};
Thread t2 = new Thread("线程2"){
@Override
public void run() {
for (int i = 1; i < 1001; i++) {
System.out.println(getName()+"-->"+i );
if (i == 100) {
try {
// 在当前线程下加入另一个线程
// 相当于"插队",直到插入的线程执行完
//t1.join();
// 让另外一个线程加入指定时间,到时后继续争抢
t1.join(10);
} catch (InterruptedException e) {
e.printStackTrace( );
}
}
}
}
};
t1.start();
t2.start();
}
守护线程
void setDaemon(boolean x)
守护线程是指,A线程守护B线程,即B是被保护,B是重要的,当B线程结束时A线程(守护线程)会立即结束
public static void main(String[] args) {
Thread hs = new Thread("皇上"){
@Override
public void run() {
for (int i = 1; i < 11; i++) {
System.out.println(getName()+"工作"+i+"天" );
}
}
};
Thread fz = new Thread("妃子"){
@Override
public void run() {
for (int i = 1; i < 101; i++) {
System.out.println(getName()+"工作"+i+"天" );
}
}
};
/**
* 不需要设置被守护,只需要设置守护线程,其他默认就是被守护线程
* 开启线程前设置
*/
fz.setDaemon(true);
hs.start();
fz.start();
}
线程休眠
- sleep(long millis)
- 会让当前线程陷入等待状态(阻塞)状态,让其资源让其他线程执行
public static void main(String[] args) throws InterruptedException {
for (int i = 10; i > 0; i--) {
System.out.println("倒计时 --> " + i );
Thread.sleep(1000);
}
System.out.println("发射!" );
}
private static void show() {
Thread t1 = new Thread("线程1"){
@Override
public void run() {
for (int i = 1; i < 101; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace( );
}
System.out.println(getName()+"-->"+i );
}
}
};
Thread t2 = new Thread("线程2"){
@Override
public void run() {
for (int i = 1; i < 101; i++) {
System.out.println(getName()+"-->"+i );
}
}
};
t1.start();
t2.start();
}
结束线程
- void stop() 结束当前线程
- void interrupt() 设置线程中断状态为以中断
- boolean isInterrupt() 测试返回线程是否中断
public static void main(String[] args) {
new Thread("线程2"){
@Override
public void run() {
for (int i = 1; i < 101; i++) {
if (i == 15){
//this.stop(); // 直接结束线程
this.interrupt();// 给线程设置一个中断状态,不会真的中断线程
// 结束不结束取决于线程本身执行情况
}
System.out.println("是否中断? "+this.isInterrupted() );
System.out.println(getName()+"-->"+i );
}
}
}.start();
}
五、线程状态[面试]
线程的几种状态:
- 创建/新建/初始
- new 完线程对象
- 就绪
- 调用start
- 等待/阻塞
- join()或者sleep()
- 运行
- 执行run()方法
- 死亡/销毁/终止
- run方法执行完,或者调用stop()或者interrupt()中断等
清楚状态之间的转换