day01笔记
1、什么是线程
线程的实现方式有很多种:
1) 内核线程=>Java中的线程,是基于内核线程的轻量级实现(轻量级进程:相比进程,创建、调度、销毁效率要高很多)
2) 用户线程:有程序自己来实现线程,包括线程的调度等等
安卓以前的开发语言是Java,最新的是kotlin(运行在jvm上的),里 面使用的就是协议(用户线程)
2、进程与线程
1、多个进程的内存是隔开的,一个进程中多个线程,可以共享内存(进程包含线程)
2、进程是系统分配的最小单位,线程是系统调度cpu执行的最小单位
3、进程的创建、销毁代价比进程小(状态转换进程的开销大与线程)
4、线程(有bug)可能会造成整个进程挂掉,进程间是独立运行的(可能存在进程通信)
3、多线程的优势/使用场景
1、充分利用cpu资源,提高执行效率
2、io等阻塞时(如果希望能同时接收输入)
4、缺陷/注意事项:
1、线程的创建/销毁也是具有一定的系统开销:所以一般用于执行耗时时间比较长的任务
2、增加编码的复杂程度:和se代码执行顺序不一样的地方,后面会学习的线程安全问题
5、线程状态和状态转移的意义
(1) new:创建态
(2) runnable:可运行态
程序无法判定某个时间到底是就绪态还是可运行态,所以这两个状态对于程 序来说没有意义
(3) 等待
(4) 超时等待
(5) 阻塞
(6) 销毁
6、Thread线程
Java中,创建线程,就是new一个Thread
常见的构造方法
7、创建线程的方式
1、 继承Thread
【继承Thread => 重写run方法(定义要执行的任务代码)】
public class ThreadDemo01 {
public static void main(String[] args) {
//继承Thread
//创建MyThread实例
MyThread myThread = new MyThread();
//运行一个线程,申请系统调度运行
myThread.start();
//继承的写法2:使用一个匿名内部类
Thread t = new Thread(){//属于继承Thread但没有名称的子类
@Override
public void run() {
System.out.println("匿名内部类run");
}
};
t.start();
}
//继承的方式 1、继承Thread 2、重写run方法(定义要执行的任务代码)
private static class MyThread extends Thread{
@Override
public void run() {
System.out.println("MyThread.run");
}
}
}
2、 实现Runnable接口
【实现Runnable接口 =》重写Runnable的run方法 =》】
//实现Runnable
public class RealizeRunnable {
public static void main(String[] args) {
//实现Runnable写法1
Thread t1 = new Thread(new MyRunnable());
t1.start();
//实现Runnable写法2:匿名内部类
Runnable r2 = new Runnable() {//属于Runnable实现类,只是没有名字
@Override
public void run() {
System.out.println("匿名内部类1 run");
}
};
Thread t2 = new Thread(r2);
t2.start();
//也可以把匿名内部类的对象直接写在构造方法的参数上(!!!常用)
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类2 run");
}
});
t3.start();
//了解:lambda表达式
Thread t4 = new Thread(() -> System.out.println("匿名内部类2 run"));
t4.start();
}
public static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("MyRunnable run");
}
}
}
3、实现Callable接口
8、线程的启动
thread.start()=》 申请系统调度,执行thread中的任务(重写的run方法)
main方法执行,也是存在一个main线程的
Thread中,start()和run()的区别
1) start:启动线程的方式
2) run:属于线程任务的描述
public class StartVsRun {
//main方法执行:
/**
* java StartVsRun:
* 1、执行java.exe进程,分配内存
* 2、创建java虚拟机,执行StartVsRun类加载
* 3、创建一个main线程,执行StartVsRun.main()
* @param args
*/
public static void main(String[] args) {
//while(true){}
//t线程,一直处于运行()
//main线程,只是执行了new Thread(new Runnable()):只是执行这两个构造方法
/*Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true){}
}
},"t线程" );
t.start();*/
//线程对象.run,只是属于普通对象的实例方法调用,没有启动线程!
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true){}
}
},"t线程" );
t.run();
}
}
9、Thread常见的属性
//获取线程属性
public class getThreadShuxing {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true){}
}
});
//后台线程:java进程,至少有一个非后台线程(用户线程)存活,进程才不会退出
//如果把t线程在启动前设置为后台进程,进程就不会管t线程是否执行完
//t.setDaemon(true);
t.start();
System.out.println("ID: "+t.getId());//ID 是线程的唯一标识,不同线程不会重复
System.out.println("名称: "+t.getName());//获取线程名称
System.out.println("状态: "+t.getState());//获取线程状态
System.out.println("优先级: "+t.getPriority());//实质写代码没用
System.out.println("后台进程:"+t.isDaemon());//是否是后台线程
System.out.println("是否存活:"+t.isAlive());//是否存活:如果启动后销毁前,都是存活
}
}
10、Thread中的常用方法
当前线程(t.join在哪个线程执行,就是谁)等待,直到线程引用执行完毕
当前线程等待,最多给定的毫秒数,或者线程引用对象死亡
t.join(3000);
//常用api
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t线程 run");
//注释掉,就是先打印t线程,在打印main
//t.join不注释:t线程一直运行态,main等待状态
//t.join(3000)不注释:t线程先打印,然后一直运行;main线程等3s后打印,然后结束
while (true){}
}
},"t线程");
t.start();
/**
* Waits for this thread to die.
* 等待,直到这个线程死亡
* 考虑:谁等待:main?谁死亡:t线程?
* 当前线程(t.join在哪个线程执行,就是谁)等待,直到线程引用执行完毕
*
*/
//t.join();
/**
* Waits at most millis milliseconds for this thread to die.
*当前线程等待,最多等待给定的毫秒数,或者线程引用对象死亡
*/
t.join(3000);
System.out.println("t线程 start,这里是main");
}
}