本月鸡汤
人之所以痛苦, 在于追求错误的东西
线程
计算机获取当前时间戳:System.currentTimeMillis()
IDEA规范代码:`ctrl+ait+l
IDEA同时操作多行: ctrl+ait+t
IDEA导入缺失包: ctrl+ait+o
线程简介
- 为了解决并发编程问题, 引入了进程, 但是由于进程在创建, 销毁, 调度时的开销太大(主要是资源分配和释放), 所以又引进了
线程
- 线程省下了资源分配和释放, 多个线程共用进程中的同一资源(
意味着多个线程只需进行一次资源分配, 资源分配以进程为单位, 一个进程包含多个线程
) - 一个进程
至少包含一个线程
, 可以包含多个线程 线程是系统调度执行的基本单位, 进程是系统资源分配的基本单位
, 进程中的第一个线程在启动时是和进程一起启动的
, 所以第一个线程在启动时需要申请资源
, 但是之后的线程不需要
- 在同一个进程中的线程共享的有
pid, 内存
(线程1new的对象在线程2中也可以使用)和文件描述符表
(线程1打开的文件线程2也可以使用) 一个线程也用PCB来描述
, 一个进程中可以有一个PCB, 也可以有多个PCB.- 一个进程中的多个PCB,其
pid, 内存指针和文件描述符表
一样, 但是他们有各自独立的状态, 优先级, 上下文, 记账信息
- 一个核心上执行一个线程
- 并
不是线程越多, 程序执行的越快
. 因为cpu的的核心是有限的, 过多的线程不仅不会增加程序运行的速度, 反而会因为频繁的线程调度而变得卡顿 - 过多的线程可能会抢占共享的内存资源或者文件描述符集, 造成
线程安全
问题.同理, 进程间进行通信时, 同时访问到同一资源时, 也会触发进程安全
问题 - 过多的线程在抢占cpu的过程中落败时,可能会使这个
线程崩溃
, 而一个线程崩溃会造成整个进程的崩溃
线程和进程的联系和区别
- 线程和进程都是为了解决并发编程问题
- 线程是系统调度执行的基本单位, 进程是系统资源分配的基本单位,
- 线程相比于进程来说, 创建, 销毁, 调度时的开销更小(更轻量)
- 进程里包含若干线程, 每个线程被独立调度执行, 一个进程里的若干线程共享进程的内存资源和文件描述表, 每个线程有自己的状态, 优先级, 上下文, 记账信息
使用Thread实现多线程编程(java)
在进行多线程编程时, 是依靠java的JVM来进行的, 操作系统给java的JVM提供api, JVM将这些api封装之后再给程序员提供api. 不同的操作系统上安装不同的JVM, JVM提供给适配当前系统的api. 使得相同的字节码文件能在不同的操作系统上运行, 从而实现跨平台编程
1. 继承thread类, 重写run:
thread用于创建线程变量, 一个thread对象对应一个PCB
class MyThread extends Thread{
public void run(){
while(true){
System.out.println("线程1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class ThreadDome1 {
public static void main(String[] args) throws InterruptedException {
Thread t = new MyThread(); //创建线程变量
t.start();
while(true){
System.out.println("线程2");
Thread.sleep(1000);
}
}
}
代码解读:
- 如果不使用Thread, 直接在main函数中打印输出, 那么就是一个进程中只有一个线程(main线程),
此线程称为主线程
- 加入了Thread之后, start是操作系统提供的api, 主线程通过调用start函数, 使操作系统来创建一个新的PCB, 并把指令提供给此PCB, 此PCB被调用到CPU上时, 就会执行run方法, 从而实现多线程, 以上代码会轮流打印线程1和2.
- start与run的区别:
(1)start 是让操作系统去创建一个新的PCB
(2)run是描述这个PCB要去执行什么, 如果在程序中直接写run, 不用start, 那么就只有一个main主线程, 这个线程在不停的打印线程1, 线程2无法打印
2. 实现Runnable接口, 重写方法
class MyRunable implements Runnable{
@Override
public void run() {
while(true){
System.out.println("线程1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
public class ThreadDome2 {
public static void main(String[] args) throws InterruptedException {
MyRunable runable = new MyRunable();
Thread thread = new Thread(runable);
thread.start();
while(true){
System.out.println("线程2");
Thread.sleep(1000);
}
}
}
3. 使用匿名内部类, 继承Thread
Thread t = new Thread(){
public void run(){
while(true){
out.println("线程1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
在上面代码中的Thread t = new Thread(){}
, 做了两件事情,
第一是创建了一个Thread的子类, 这个子类没有名字
第二是创建了子类的实例, t引用了这个实例
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
import static java.lang.System.out;
public class ThreadDome3 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(){
public void run(){
while(true){
out.println("线程1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new