java多线程(一)

线程和进程是什么

1.进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
2.线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。

线程和进程的区别

1.一个线程只能属于一个进程,但是一个进程可以拥有多个线程。多线程处理就是允许一个进程中在同一时刻执行多个任务。
2.线程是一种轻量级的进程,与进程相比,线程给操作系统带来侧创建、维护、和管理的负担要轻,意味着线程的代价或开销比较小。
3.线程没有地址空间,进程的地址空间包含线程。线程上下文只包含一个堆栈、一个寄存器、一个优先权,线程文本包含在他的进程的文本片段中,进程拥有的所有资源都属于线程。所有的线程共享进程的内存和资源。
4.同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段, 寄存器的内容,栈段又叫运行时段,用来存放所有局部变量和临时变量。

进程的创建

1.在Windows系统中,创建进程的开销很大,因此Windows鼓励大家创建多线程。多线程学习重点是要大量面对资源争抢与同步方面的问题。
2.Linux系统中,创建进程的开销很小,因此Linux鼓励大家创建多进程。Linux的学习重点是要学习进程间通讯的方法。

线程的状态

1.创建状态(就是我们创建了线程,但是还没有运行start方法)
2.就绪状态(运行了start方法,但是由于系统的任务调度问题,没有获取到资源)
3.运行状态(得到了系统资源,开始运行run方法了)
4.销毁状态(运行结束,或者直接运行stop方法被迫结束)
5.等待状态(使用了sleep方法和wait方法,让线程让给系统资源,等待唤醒)

多线程的方式

1.继承Thread类

1
2
3
4
5
6
class Test1 extends Thread{
@Override
public void run() {
//要执行的方法
}
}

2.实现Runnable接口

1
2
3
4
5
6
class Test1 implements Runnable{
@Override
public void run() {
//要执行的方法
}
}

3.匿名内部类

1
2
3
4
5
6
new Thread(new Runnable(){
@Override
public void run() {
//要执行的方法
}
});

4.线程池
5.callable

线程的启动

1.直接调用run()方法。
2.调用start()方法。

两种方法的区别:
1.直接使用run()方法就如调用普通的方法一样,并没有创建新线程。
2.调用start()方法就会创建新的线程,主线程的结束不会影响新开的线程。

守护线程和非守护线程

线程有分守护线程和非守护线程。
1.守护线程里有:主线程和gc线程(主线程就是Java中的main方法,gc线程是指Java垃圾回收中的线程)
2.非守护线程里有:用户自定义的线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args){
        Thread thread = new Thread(new Runnable(){

            @Override

            public void run() {

                for (int i = 0; i < 90; i++){

                    System.out.println(i);

                }

            }

        });
        thread.start();
    }

在我写代码的时候这样写会出现一个问题,那就是要么打印台什么都不打印,要么打印台就打印一部分,没有完全打印。这就是创建线程时默认创建守护线程。
我们可以通过 thread.setDaemon(false); 转成非守护线程,这样主线程结束后我们的线程依然打印完数字,没有被影响。

Thread和Runnable源代码

我在看源代码的时候看到了native关键字,使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。详细的后面专门来看。

在源码中我们可以看到Runnable接口里面就只有一个run的抽象方法
在Thread里面可以看到很多方法,run()方法里面判断了构造函数设置的线程类是否存在,如果存在就调用方法,不存在就调用父类方法。里面还可以知道我们获取的currentThread方法是static和native的,是使用c语言来实现的。还有getId方法获取线程的id时,其实是有一个静态的变量来进行计数的,在线程创建的时候,构造方法调用后,会将这个变量加一,然后赋给这个线程类,再加入到线程列表里面。