每个进程至少有一个线程存在,即主线程(系统级别的,C语言的主线程)
Java级别的主线程:自己写的入口函数main方法(可以没有这个线程)
线程与进程的区别:
(1)进程是系统分配资源的最小单位,线程是系统调度的最小单位
(2)进程有自己的内存地址空间,线程只独享指令流执行的必要资源,如寄存器和栈
(3)由于同一进程的各线程之间共享内存和文件资源,可以不通过内核进行直接通信
(4)线程的创建、切换及终止效率更高(线程的创建、切换、终止也比较耗时,这里只是相对于进程来说效率更高)
静态内部类:
package lesson1;
public class InnerClass {
//静态内部类,和普通类使用没有什么区别,只是需要明确是哪个类的内部类
//在其他包中使用时:InnerClass.A()
public static class A{}
}
package ll;
import lesson1.InnerClass;
public class B {
public static void main(String[] args) {
//静态内部类的调用
new InnerClass.A();
}
}
******************************************************************************************************************
创建线程的方法:
package lesson1;
public class ThreadLook1 {
static class MyThread extends Thread {
@Override
public void run() {
System.out.println("创建线程方法一:继承Thread类");
}
}
static class MyThread1 implements Runnable{
@Override
public void run() {
System.out.println("创建线程方法二:实现Runnable接口");
}
}
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
Thread t1 = new Thread(new MyThread1());
t1.start();
}
public static void main1(String[] args) {
//创建线程 方法三
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("使用匿名类创建Thread子类对象");
}
};
t1.start();
//创建线程 方法四
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名类创建Runnable子类对象");
}
});
t2.start();
//创建线程 方法五 使用lambda表达式创建Runnable子类对象
Thread t3 = new Thread(() -> System.out.println("使用匿名类创建Thread子类对象"));
Thread t4 = new Thread(() ->{
System.out.println("使用匿名类创建Thread子类对象");
});
}
}
******************************************************************************************************************
Thread常见的使用方法
方法 | 说明 |
---|---|
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用Runnable对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target,String name) | 使用Runnable对象创建线程对象,并命名 |
示例:
Thread t1 = new Thread();
Thread t2 = new Thread(new MyThread1());
Thread t3 = new Thread("线程名称");
Thread t4 = new Thread(new MyThread1(),"线程名称");
******************************************************************************************************************
Thread常见的属性
属性 | 获取方法 |
---|---|
ID | getId()–线程的唯一标识,不会重复 |
名称 | getNmae() |
状态 | getState() |
优先级 | getPriority() --优先级高的线程理论上更容易被调度到 |
是否后台(守护)线程 | getDaemon()–JVM会在一个进程的所有非后台线程结束后才会结束运行 |
是否存活 | isAlive()–run方法是否结束 |
是否被中断 | isInterrupted() |
示例:
package lesson1;
public class ThreadLook4 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() { //子线程
@Override
public void run() {
while (true) {
}
}
},"main子线程"); //命名
t.start();
System.out.println(t.getId()); //12
System.out.println(t.getName()); //main子线程
System.out.println(t.getState()); //RUNNABLE
System.out.println(t.getPriority()); //5
System.out.println(t.isDaemon()); //false
System.out.println(t.isAlive()); //true
System.out.println(t.isInterrupted()); //fals
}
}
运行结果如下:注意此时线程还未结束运行(关于守护线程的测试)
当在t.start()线程启动之前使用t.setDaemon(true)设置守护线程之后再运行程序,程序结束运行。也就是说,对于Java线程,只要有一个非守护线程还没有终止,进程就不会结束。
同时对于Thread.currentThread().getName 和 t.getName() this.getName()三者之间的不同需要注意
总结:
Thread提供静态方法currentThread()来供我们调用,可以避免this无法获取到main线程的问题。
通过继承Thread 创建线程时,可以使用this关键字去调用继承自父类Thread的方法,this就是当前的对象。
Thread.currentThread()可以获取当前线程的引用,一般都是在没有线程对象又需要获得线程信息时通过Thread.currentThread()获取当前代码段所在线程的引用,且通过Runnable接口实现线程创建时,在run()里面只能使用Thread.currentThread()
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
System.out.println(this.getName() + " Thread + this + run"); //Thread-0 Thread + this + run
//System.out.println(t.getName()); //编译错误
System.out.println(Thread.currentThread().getName() + " Thread + current + run"); //Thread-0 Thread + current + run
}
};
t.start();
//System.out.println(this.getName); //编译错误
System.out.println(t.getName() + " Thread + t.getName"); //Thread-0 Thread + t.getName
System.out.println(Thread.currentThread().getName() + " Thread + current"); //main Thread + current
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//System.out.println(this.getName); //编译错误
//System.out.println(t1.getName()); //编译错误
System.out.println(Thread.currentThread().getName() + " Runnable + current + run"); //Thread-1 Runnable + current + run
}
});
t1.start();
//System.out.println(this.getName); //编译错误
System.out.println(t1.getName() + " Runnable + t.getName"); //Thread-1 Runnable + t.getName
System.out.println(Thread.currentThread().getName() + " Runnable + current"); //main Runnable + current
Thread.currentThread().setName("主线程");
System.out.println(Thread.currentThread().getName()); //主线程
}
start() VS run() —区别
基础认识:代码在哪个线程执行,就是由哪个线程执行代码行
start() 会向系统申请启动某个线程,如果该线程处于运行状态,会自动执行run()
run() 通过 Thread 或者 Runnable 类定义要执行的任务代码,即线程再运行态执行的代码 如果不调用start() 而直接调用 run()[线程体] ,相当于Java对象直接调用普通实例方法.
start 启动线程 ,线程处于就绪态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这时此线程处于运行态,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。
******************************************************************************************************************
PS:
如何同时启动多个线程?
代码如下:
package lesson1;
public class ThreadLook2 {
public static void main(String[] args) {
//同时启动20个线程 每个线程从 0+1的方式加到99;
for (int i = 0; i < 20;i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() { //在多线程环境下,即使同一段代码块,也是可以并发并行执行
for (int i = 0; i < 100;i++) {
if (i == 99 ) {
System.out.println(i);
}
}
}
});
t.start();
}
}
}
注意:某个线程抛run异常 整个线程结束 但是不会影响其他线程
package lesson1;
public class ThreadLook3 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() { //子线程
@Override
public void run() {
for(int i = 0; i < 10;i++) {
if(i == 6) {
//某个线程抛run异常 整个线程结束 但是不会影响其他线程
//线程中处理异常的方式 线程对象.setUncaughtExceptionHandler()
//或者自己在run()方法里捕获
throw new RuntimeException();
}
System.out.println(i);
}
}
},"main子线程"); //命名
t.start();
while (true) { //main线程
}
//子线程结束 main线程继续
}
运行结果如下:此时虽然run线程结束,但是main线程还没有结束