线程
1 线程概述
1.1 什么是线程
v 线程是程序执行的一条路径, 一个进程中可以包含多条线程
v 一个应用程序可以理解成就是一个进程
v 多线程并发执行可以提高程序的效率, 可以同时完成多项工作
1.2 多线程应用场景
VNC同时共享屏幕给多个电脑
迅雷开启多条线程一起下载
QQ同时和多个人一起视频
服务器同时处理多个客户端请求
1.3并行和并发的区别
并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于间时间隔较短,使人感觉两个任务都在运行(画图-任务调度)。
1.4 Java程序运行原理
Java命令会启动java虚拟机(JVM),等于启动了一个应用程序,也就是启动了一个进程。
该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法
一个应用程序有且只有一个主线程,程序员不能New主线程,可以New子线程。
1.5 JVM启动的是多线程吗?
JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
main方法的代码执行的位置就是在主线程(路径)
一个进程有多个线程
finalize()这个方法在子线程(垃圾回收线程)执行
public class Demo01 {
public static void main(String[] args) {
/*JVM的启动是多线程的吗?【面试题】*/
System.out.println("AAAAA");
System.out.println("BBBBB");
System.out.println("CCCCC");
System.out.println("DDDDD");
//打印线程名称
System.out.println(Thread.currentThread());//主线程
for(int i = 0;i<2;i++){
new Student();
System.gc();//启动垃圾回收
}
}
}
class Student{
//被垃圾回收器回收时,会调用
//对象从内存释放时,会调用
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
System.out.println("student 被回收了...");
//打印线程名称
System.out.println(Thread.currentThread());//子线程
}
}
2 Java中线程的实现方式
2.1方式一、继承Thread
使用步骤:
1.定义类继承Thread
2.重写run方法
3.把新线程要做的事写在run方法中
4.创建线程对象
5.开启新线程, 内部会自动执行run方法
代码:
public class Demo01 {
public static void main(String[] args) {
/*主线程,程序员不能创建,程序员只能创建子线程*/
//1.创建子线程对象
MyThread t1 = new MyThread();
/**不能通过下面的方式来执行任务
* 因为这种试的任务是在主线程执行的*/
//t1.run();
//2.正确的执行任务的方式,调用start,内部会开启新线程,调用run方法
t1.start();
//3.再创建子线程
MyThread t2 = new MyThread();
t2.start();
//4.循环创建子线程
for(int i=0;i<10;i++){
MyThread th = new MyThread();
th.start();
}
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("银行信用卡还款短信任务..." + Thread.currentThread());
System.out.println("线程名称" + this.getName());
}
}
2.2方式二、实现Runnable接口
实现步骤:
1.定义类实现Runnable接口
2.实现run方法
3.把新线程要做的事写在run方法中
4.创建自定义的Runnable的子类对象,创建Thread对象传入Runnable
5.调用start()开启新线程, 内部会自动调用Runnable的run()方法
代码:
public class Demo01 {
public static void main(String[] args) {
/* 线程实现的方式 (2) - 定义类实现Runnable接口
//1.创建runable对象
BankTask task = new BankTask();
//2.创建Thread对象
Thread t1 = new Thread(task);
//3.启动线程
t1.start();
//4.再开启2个线程
Thread t2 = new Thread(task);
t2.start();
Thread t3 = new Thread(task);
t3.start();
}
}
class BankTask implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("银行储蓄卡自动结算利息任务..." + Thread.currentThread());
//System.out.println("线程名称:" + this.getName());
System.out.println("线程名称:" +Thread.currentThread().getName());
}
}
2.3两种方式的区别
区别:
继承Thread : 由于子类重写了Thread类的run(), 当调用start()时直接找子类的run()方法
实现Runnable : 构造函数中传入了Runnable的引用, 有个成员变量记住了它, 调用run()方法时内部判断成员变量Runnable的引用是否为空。
继承Thread
好处是:可以直接使用Thread类中的方法,代码简单
弊端是:如果已经有了父类,就不能用这种方法
实现Runnable接口
好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,代码更灵活
弊端是:不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到Thread的方法,代码复杂
2.4 匿名内部类实现线程的两种方式
public static void main(String[] args) {
//匿名内部类实现线程的两种方式
/*Thread t1 = new Thread(){
@Override
public void run() {
System.out.println("任务1...." + Thread.currentThread());
}
};
t1.start();*/
new Thread(){
public void run() {
System.out.println("任务1...." + Thread.currentThread());
};
}.start();
/*Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任务2...." + Thread.currentThread());
}
});
t2.start();*/
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任务2...." + Thread.currentThread());
}
}).start();
}
2.5 获取线程名字和设置名字
通过Thread的getName()方法获取线程对象的名字
通过setName(String)方法可以设置线程对象的名字
通过构造函数可以传入String类型的名字
每个线程系统都会默认分配个名字,主线程:main,子线程thread-0 ....
public class Demo01 {
public static void main(String[] args) {
/* 获取线程名字和设置名字(掌握)
//1.获取主线程对象
Thread mainThread = Thread.currentThread();
System.out.println(Thread.currentThread());
System.out.println(mainThread);
System.out.println("名称:" + mainThread.getName());
//2.设置线程的名称
mainThread.setName("主线程");
System.out.println(mainThread);
//3.设置子线程的名称
MyThread myThread = new MyThread("子线程1");
myThread.start();
}
}
class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println("银行代发工资任务..." + Thread.currentThread());
}
}
2.6 获取当前线程的对象
Thread.currentThread()方法用于获取当前线程对象
在不同的方法中,获取的线程对象名称是有可能不一样的
在main中获取的是主线程对象
在子线程的run方法中获取的是子线程对象
public class Demo01 {
public static void main(String[] args) {
//获取当前线程的对象(掌握)
Thread mainThread = Thread.currentThread();
mainThread.setName("主线程");
//打印主线程对象
System.out.println(mainThread);
//打印主线程对象类名
System.out.println(mainThread.getClass());
System.out.println("================");
//开启子线程
MyThread mt = new MyThread();
mt.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("任务...");
Thread subThread = Thread.currentThread();
//打印子线程对象
System.out.println(subThread);
//打印子线程对象类名
System.out.println(subThread.getClass().getName());
}
}