一.进程和线程
进程:一个正在运行的程序就是一个进程
线程:一个线程中 有很多个线程(每一个线程相当于一个执行的任务)
CPU会随即在多个线程中进行切换 分时调度
CPU会在多个线程中进行随机切换
相当于在程序中进行快速切换(CPU运算很快)
开启一个线程 相当于开启了一个CPU的执行路径
CPU在执行多个线程的时候是随机的 跟线程的优先级有关
单线程程序的特点:
程序由上至下一次执行(一个执行完 才会去执行下一个)
好处:绝对安全 不牵扯到操作共享数据
弊端:效率不高
二.主线程
多线程的程序 除了主线程外 一般都叫子线程 且一个程序只有一个主线程
我们常用的main函数 就是一个线程 并且是主线程
主线程(叫main的线程)是如和被执行的
JVM调用main函数 --> CPU就为叫main的函数开辟了一个执行路径 --> 相当于在这个执行路径中 执行main函数中的代码
Thread 线程类 (可以开辟一个主线程以外的线程 子线程)
1.创建一个Thread类的子类
2.重写run方法
run方法就是你要线程执行的代码
3.调用start方法 开启线程(不能重复开启)
例:
之前我们出现使用过的一个代码
public static void main(String[] args) {
System.out.println(10/0);
}
在报错时显示的是:
Exception in thread "main" 意思是异常出现在叫main的这个线程中
1.线程类
声明一个线程类
class SubTread extends Thread{
//重写run方法
@Override
public void run() {
//Exception in thread "Thread-0"
//子线程默认的名字 Thread-x
//x是从0开始的
System.out.println(10/0);
//子线程 执行的代码
for (int i = 0; i < 100; i++) {
System.out.println("run---" + i);
}
System.out.println("我是子线程");
}
}
在执行
System.out.println(10/0);
时,会报出一个异常Exception in thread "Thread-0"
子线程默认的名字Thread-x x是从0开始的
在主线程中创建线程类对象 即创建一个子线程
public static void main(String[] args) {
//开启多个线程时 多个线程 会同时执行
//并且是相对的对立执行单元
SubTread subTread = new SubTread();
//开启
subTread.start();
SubTread subTread1 = new SubTread();
subTread1.start();
//主线程出现异常了 但是并没有影响子线程的执行
System.out.println(10/0);
for (int i = 0; i < 100; i++) {
System.out.println("main---" + i);
}
}
这里执行
System.out.println(10/0);
时,也会出现异常 但不会影响子线程的执行 说明主线程和子线程是相对独立的
三.线程的名字
在重写的run方法中
//在哪获取的就是哪个线程的对象
Thread currentThread = Thread.currentThread();
System.out.println("我是子线程" + currentThread.getName());
获取的就是重写这个run方法的子线程的名字
也可简写为:
System.out.println("我是子线程" + Thread.currentThread().getName());
如果直接在在主线程中写
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName());
获取的就是当前正在执行的线程的名字 即主线程的名字
在主线程直接调子线程的run方法 相当于调用了一个普通的成员方法
而调用start方法 系统会开辟一个线程出来
练习:
创建一个线程类
类中有个name属性 并提供有参无参 set/get方法
class Mythread extends Thread{
//定义了自己的name属性
private String name;
public Mythread() {
}
//有参的构造方法
//1.给父类的name属性赋值
//2.给本类的name属性赋值
public Mythread(String name,String myName) {
super(name);
//给本类的name赋值
this.name = myName;
}
}
但是在写set/get方法时 会报错
因为在父类Thread类中使用了final修饰了该方法 不能被子类重写
所以需要改一下set/get的方法名(只要和默认名字不一样就可以) 来重新提供set/get方法
另外
创建一个线程相当于CPU开辟了一个独立的执行路径
相当于开辟了一个独立的方法栈区 来专门运行子线程中的方法
四.创建线程的方法二
用接口实现类的方式来创建
创建一个类 来实现Runnable接口
class RunnableImpl implements Runnable{
//测试该方法能不能执行 是不是在子线程中执行
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
}
在主线程中用创建接口的实现类的对象来创建子线程
RunnableImpl rImpl = new RunnableImpl();
//创建一个线程类对象
Thread thread = new Thread(rImpl);
//run方法不能开启线程 start方法才能开启线程
//thread.run();
thread.start();
可以把两步合起来写:
直接在主线程中创建:
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程执行的方法");
}
});
五.创建线程的方法三
匿名内部类的方式创建
匿名内部类创建出来的对象是该类的子类对象
接口和类都可以用这种方法
1.类
class Test{
public void fun() {
System.out.println("我是Test类中的fun方法");
}
}
在主线程中用匿名内部方法创建线程的写法:
Test test = new Test() {
//重写fun方法
@Override
public void fun() {
System.out.println("我是匿名内部类中重写的fun方法");
}
};
test.fun();
new Test() { };相当于是Test类的子类对象
并且在后面的大括号中 可以重写父类的方法
2.接口
interface TestInter{
public abstract void fun();
}
直接创建接口类的实现类对象
TestInter testInter = new TestInter() {
@Override
public void fun() {
System.out.println("我是实现类的fun方法");
}
};
testInter.fun();
也可以写成:
new TestInter() {
@Override
public void fun() {
System.out.println("我是实现类的fun方法");
}
}.fun();
注意:这种方式 new后面跟的是父类名
但创建出来的对象 却是子类或实现类的对象
六.线程的休眠
测试线程代码时比较常用
线程的状态大致分为六种:
1.新建状态
new thread()
2.运行状态
start()
3.死亡状态
run方法运行完毕的状态
(也可以直接用stop方法进入此状态 不过已过时 不推荐用)
4.受阻塞状态
线程调用了start方法 就一定进入运行状态吗?不一定!
调用start方法是表示该方法可以被CPU执行 但是CPU不一定来执行此进程 所以不一定变成运行状态 需要等待CPU的执行
这种状态叫做受阻塞状态
5.休眠状态
sleep方法
休眠结束 可能没有CPU执行 也可能进入受阻塞状态
6.等待状态
wait方法进入等待状态
notify方法可以唤醒线程
休眠:
主线程休眠是在主线程中调用sleep方法
参数是休眠时间 单位是毫秒
休眠时间结束 会自动醒来
Thread.sleep(1000);
System.out.println("aaaa");
子线程的休眠:
class TestA extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//休眠
//父类中的run没抛异常
//子类就只能try/catch处理
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
}
在主线程中创建子线程
TestA testA = new TestA();
testA.start();
结果会让线程卡住1秒 再打印代码的内容