一,多线程
并行与并发
并行: 两个事件,在同一个时刻,都在发生
并发: 两个事件,在同一个时间段内,都在发生(交替执行)
进程与线程
进程: 正在内存中运行的程序,我们称为进程
线程: 进程中完成某个小功能的模块(进程中用执行某个功能的执行单元)
线程是属于某个进程的
每个进程都有独立的内存空间(独立的栈独立的堆等),并且至少有一个线程
每个线程都会跟进程申请一块独立栈,共享进程的堆
线程调用是指CPU在不同的进程不同的线程之间进行快速切换
线程调度的分类:
分时调度: 每个线程平均拥有CPU的执行权
抢占式调用: 每个线程随机分配CPU的执行权(具体的分配多少和线程优先级有关)
我们Java程序(Java进程)中所有线程采用抢占式调度
线程的状态
Thread对象共有6种状态:NEW(新建),RUNNABLE(运行),BLOCKED(阻塞),WAITING(等待),TIMED_WAITING(有时间的等待),TERMINATED(终止);状态转换如下:
1.Thread类
a.Thread类是什么?
Thread是Java定义好的,代表线程的类,只要创建该类的一个对象,其实就是创建了一个线程
b.Thread类的构造方法
public Thread(); // 无参构造,线程会有默认的名字,Thread-0,Thread-1等...
public Thread(String name); //带有线程名字的构造
public Thread(Runnable r);//带有线程任务的构造
public Thread(Runnable r,String name); //即带有线程名字,又带有线程任务的构造
c.Thread类的成员方法
public String getName(); //获取线程的名字
public void setName(String name);//修改线程的名字
public void run();//代表线程要执行的任务,任务有关的代码需要写在次方法中
public void start();//线程只创建并不会执行,必须调用start开启后才会执行任务
public static void sleep(long millis); //让当前线程"休眠/暂停"多少毫秒
这里的当前线程只指 Thread.sleep(1000)这句代码写哪个线程中,哪个线程就是当前线程
public static Thread currentThread();//获取当前线程对象
这里的当前线程是指 Thread.currentThread() 这句代码写哪个线程中,哪个线程就是当前线程
线程执行有优先级,优先级越高先执行机会越大(并不是一定先执行!!)。优先级用int的priority参数表示。
线程优先级最高为10,最低为1。默认为5
2.创建新的线程的方式-继承
a.描述:
将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例
b.分析创建的步骤:
i.创建子类 继承 Thread
ii.子类中重写run方法(在run中编写线程要执行的任务代码)
iii.创建子类对象(实际上就是创建一个线程对象)
iv. 调用线程对象的start方法(启动该线程)
c.案例:
//i.创建子类 继承 Thread
public class MyThread extends Thread {
//ii.子类中重写run方法(在run中编写线程要执行的任务代码)
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("子线程..."+i);
}
}
}
public class ThreadDemo02 {
public static void main(String[] args) {
// iii.创建子类对象(实际上就是创建一个线程对象)
MyThread mt = new MyThread();
//iv. 调用线程对象的start方法(启动该线程)
mt.start();
//主线程 不会等待子线程任务结束
for (int i = 0; i < 50; i++) {
System.out.println("主线程..."+i);
}
}
}
注意:
a.我们可以给线程起名字,也可以使用默认的名字
b.我们获取线程的名字时:
建议使用通用方式: Thread.currentThread().getName();
如果是子线程内部也可以直接调用getName()获取子线程的名字
创建新的线程方式二_实现方式
a.描述
声明实现 Runnable 接口的类。该类然后实现 run 方法。
然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递 并启动.
b.分析步骤:
i.创建实现类 实现 Runnable接口(实际上接口中一个任务方法,run方法)
ii.实现类重写run方法(run中编写具体的任务代码)
iii.创建实现类对象(该实现类对象并不是线程对象,我们称为任务对象)
iv. 创建Thread对象,同时传入实现类对象
public Thread(Runnable r);//带有线程任务的构造
v. 启动该线程(调用线程对象的start方法)
c.代码实现
//i.创建实现类 实现 Runnable接口(实际上接口中一个任务方法,run方法)
public class MyRunnable implements Runnable {
//ii.实现类重写run方法(run中编写具体的任务代码)
@Override
public void run() {
//run中写任务代码
for (int i = 0; i < 50; i++) {
System.out.println("子线程..."+i);
}
}
}
public class TestThread {
public static void main(String[] args) {
//iii.创建实现类对象(该实现类对象并不是线程对象,我们称为任务对象)
MyRunnable mr = new MyRunnable();
//iv. 创建Thread对象,同时传入实现类对象
Thread tt = new Thread(mr);
//v. 启动该线程(调用线程对象的start方法)
tt.start();
//主线程不会等待子线程执行完毕
for (int i = 0; i < 50; i++) {
System.out.println("主线程..."+i);
}
}
}
两种方式的优劣比较
两种创建线程的方式,实现方式比较好
a.实现方式比较好,因为实现方式线程和任务是分开,是由程序员自己组合
b.实现方式避免了Java单继承不足
c.实现方式线程和任务是解耦的,继承方式线程和任务是耦合的
d.对于线程池来说,我们需要的是Runnable的实现类,而不需要Thread的子类
综上所述: 在开发中我们建议使用实现方式(并不是说继承方式不对)
匿名内部类简化创建线程方式
匿名内部类作用:
可以快速创建一个类的子类对象或者一个接口的实现类对象
public class TestDemo {
public static void main(String[] args) {
//1.继承方式创建线程
new Thread(){
@Override
public void run(