认识线程
线程概念
首先多进程已经很好的实现了并发编程的效果,但是有一个很明显的缺点:进程太重(1.资源消耗更多2.速度更慢),如果需要大规模的创建和销毁进程,开销就会很大,具体体现在需要给进程分配资源,而线程就解决了不去分配内存硬盘资源问题,只需要分配一个简单的pcb.
在创建的时候,把资源分配好,后续创建的线程,让线程在进程内部(进程之间的关系,可以认为是进程包含了线程),后续在进程中的新线程中,直接复用前面进程创建的好的资源,其实一个进程,至少包含了一个线程存在,即主线程,最初创建的时候,可以认为是一个只包含了一个线程的进程(此时创建的时候需要资源分配,创建的开销会比较大),但是后续在这个进程里面创建线程,就可以省略分配资源的过程,这些线程在各自独立的cpu上进行调度.因此,线程就既可以完成"并发编程",又可以以比较轻量的方式来运行.
进程和线程的区别和联系
进程和线程的区别和联系
- 进程包含线程,都是为了实现并发编程的方式,线程比进程更轻量.
- 进程是系统分配资源的基本单位,
线程是系统调度执行的基本单位
创建进程的时候已经,操作系统会分配独立的资源(主要是虚拟地址空间和文件描述符表),后续创建线程的时候,直接共享进程的资源即可- 每个进程都有自己独立的地址空间,不同进程之间无法直接访问彼此的内存空间,进程之间相对独立,一个进程的崩溃不会直接影响到其他线程.
同一进程内的线程可以共享资源,通信和数据共享比较容易,线程的创建,销毁开销小,可以共享进程的上下文.但是由于线程共享一份地址空间,就需要注意线程间的同步和互斥,避免发生争端,一个线程的问题可能会影响整个进程的稳定性.
如何创建线程
多线程程序和普通程序的区别:
-
每一个线程都是一个独立的执行流.
-
多线程之间都是并发关系.
- 继承Thread类
1 ) 继承Thread来创建一个线程类
- 继承Thread类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("运行线程的的代码");
}
}
2 )创建Mythread类的实例
MyThread t = new MyThread();
3 )调用start方法启动线程
t.start();//线程启动
class Mythread extends Thread{
//对run方法重写,run相当于线程的入口,描述具体做什么事
@Override
public void run() {
System.out.println("hello world");
}
}
//写一个简单的hello world
public class demo1 {
public static void main(String[] args) {
Mythread mythread = new Mythread();
//start就是在创建线程,会在底层调用操作系统的API,同时操作系统内核会创建出对应的pcb结构,这个创建出来的线程会参与到cpu的调度中,执行的工作是重写run方法
mythread.start();
//我们使用run方法,也会打印出hello world,但是并没有调用系统api,也没有创建出线程
mythread.run();
}
}
class Mythread extends Thread{
@Override
public void run() {
while (true) {
System.out.println("hello world");
}
}
}
public class demo1 {
public static void main(String[] args) {
Mythread mythread = new Mythread();
//会创建一个新的线程执行run方法,主线程继续执行说明两个线程时并发执行,都能参与到cpu的调度中
mythread.start(); //交替打印hello world和hello
//只有一个线程,调用run方法时,必须把run执行完才能继续往下执行.
mythread.run();//一直打印hello world
while (true) {
System.out.println("hello main");
}
}
}
当我们运行程序的时候,就会先创建一个Java进程,这个进程中就至少包含了一个线程,这个线程就叫做主线程也就是执行main方法的线程.
- 实现Runable接口
1 )实现Runable接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("这里是线程运行的代码");
}
}
2 )创建Thread对象,把runnable作为参数传到Thread构造方法
Thread t = new Thread(new MyRunnable());
3 ) 调用 start 方法
t.start();//线程启动
class MyRunnable implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
//这里只能try...catch不能throw
//此处是方法重写,对于父类run方法来说,没有throw xxx异常这样的设定,因此再重写的时候,也就不能throw
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class demo2 {
public static void main(String[] args) throws InterruptedException {
MyRunnable runnable = new MyRunnable();
//创建Thread对象,把runnable作为参数传到Thread构造方法
Thread t = new Thread(runnable);
t.start();
while (true) {
System.out.println("hello main");
//主线程调用Thread.sleep方法时,会先暂停当前线程执行的任务,等待时间以毫秒为单位,
//如果指定的时间超过了指定的时间间隔(这种情况为1秒),线程就会一直等待,直到时间到达为止
Thread.sleep(1000);
}
}
}
对比上面两种方法:
- Thread直接把要完成工作,放到了Thread的run方法中
- Runnable则是分开操作,把要完成的工作放到Runnable中,再让Runnable和Thread配合,这就使得Runnable把线程要执行的任务和线程本身,进一步的解耦合了.
其他创建线程的方式
- 匿名内部类创建Thread子类对象
public class demo1 {
public static void main(String[] args) {
//创建子类.子类继承自Thread,子类没有名字,并且类的创建实在demo1这个类里面
//创建了子类实例,使用t这个引用来指向
Thread t = new Thread() {
@Override
//在子类中重写run方法
public void run() {
while(true) {
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
t.start();
}
}
- 匿名内部类创建Runnable子类对象
public class demo2 {
public static void main(String[] args) {
//创建Runnable的子类(类,实现Runnable),通过构造方法传给Thread
Thread t = new Thread(new Runnable() {
@Override
//重写run方法
public void run() {
System.out.println("hello");
}
});// ")"对应的时Thread构造方法的结束
t.start();
}
}
- lambda表达式创建Runnable子类对象
public class demo3 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("hello");
});
t.start();
}
}