进程与线程(一)(基本定义和demo)

≡放荡痞女 提交于 2020-03-19 07:26:32
线程和进程
 
进程定义
进程指正在运行的程序 确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。(比如电脑中运行的酷狗,qq等)
线程定义
线程是进程中的一个执行单元,负责当前进程中程序的执行,线程共享进程的资源。
线程与进程的关系
一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
主线程
jvm启动后,必然有一个执行路径(线程)从main方法开始的,一直执行到main方法结束,这个线程在java中称之为主线程。
多线程定义
即就是一个程序中有多个线程在同时执行。多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。提到多线程就要提到两个概念:串行/并行
串行
单个线程执行多个任务。比如下载文件,第一个没下完不能开始下载第二个,属于不同时刻。缺点很明显,效率很低。
并行
多个线程执行多个任务。比如开启迅雷同时下载好几个文件,属于同一时刻。效率很高,但是要注意不要每个任务都建一个线程去处理,任务数量越多,内存压力越大,严重会导致宕机。
程序运行原理
分时调度
所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
抢占式调度
优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个线程(线程随机性),java使用的为抢占式调度。
 
使用线程和不适用线程的区别(一个简单的例子)
 
不使用线程
代码如下:
public static void main(String[] args) {
    ThreadTest();
    System.out.println("abc");
}

public static void ThreadTest(){
    for(int i=0;i<10000;i++){
        System.out.println(i);
    }
}

执行main方法可以看到输出结果是按照顺序先执行ThreadTest方法,输出1--9999的数字,之后输出的abc(从上往下输出)

效果如下:

使用线程

创建线程有三种方法

注意:多次启动一个线程使非法的,特别是当线程已经结束执行后,不能再重新启动。

1. 继承Thread类,重写Thread类的run方法

创建步骤:

(1)定义一个类继承Thread

(2)重写run方法

(3)创建子类对象,就是创建线程对象。

(4)调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。

public static void main(String[] args) {
    SubThread st = new SubThread();//创建一个继承了Thread类的对象
    //st.run();//如果直接调用run,还是单线程程序
    st.start();//调用start方法,是多线程程序
    for(int i=0;i<50;i++){
        System.out.println("main"+i);
    }
}

//继承Thread类,重写run方法
public class SubThread extends Thread{
    public void run(){
        for(int i=0;i<50;i++){
            System.out.println("run:"+i);
        }
    }
}

执行main方法,可以看到并不是如单线程一样从上到下执行的。

start方法的作用:使该线程开始执行,Java虚拟机调用该线程的run方法。

效果:

总结一下:

1. 上面的程序,其实是两个线程,一个是运行main方法的主线程,一个是运行run方法的线程,这两个线程都会被CPU选择(随机选择)。

2. 为什么要继承Thread类呢?因为Thread类是用来描述线程,具备线程该有的功能。那么为什么不直接创建Thread类的对象呢?比如,Thread t = new Thread(); t.start()这种写法。首先要说这么写没有问题,调用start()方法的时候也会去执行run方法,但是这个run方法是Thread类里的run方法,里面没有我们想要运行的代码,我们也不能将我们要运行的代码写到这里。

3. 创建线程的目的是什么?是为了建立程序独立的执行路径,让多部分代码实现同时执行。也就是说线程创建并执行需要给定线程要执行的任务。对于之前主线程,它的任务定义在main函数中。自定义的线程需要执行的任务都定义在run方法中。

4. 线程对象调用run方法和start方法的区别?

(1)调用run方法不开启线程,仅仅是对象调用方法。

(2)调用start方法开启线程,并让jvm调用run方法在开启的线程中执行。

5. 多线程执行得时候,在内存中是如何运行的呢?

多线程执行的时候,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。也可以说栈内存都是线程私有的。当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。

 

2. 实现Runnable接口,实现run方法。

实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。

创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。

实现Runnable的好处。既避免了单继承的局限性,Runnable接口对线程对象和线程任务进行解耦。

public static void ThreadTest(){
    SubRunnable sr = new SubRunnable();
    Thread t = new Thread(sr);
    t.start();
    for(int i=0;i<10000;i++){
        System.out.println("main"+i);
    }
}

public class SubRunnable implements Runnable{
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println("run"+i);
        }
    }
}

3. 实现Callable接口。

具体的使用方法参照下篇文章线程池中的案例。

 

线程的名字

 

1. 线程名字的获取(getName和currentThread方法)

(1)主线程名称就是main。因为getName()方法是非静态的,并且静态方法(mian)是不允许调用非静态的方法的,所以main中不能直接写getName方法,需要写currentThread方法

public static void main(String[] args) {
        System.out.println(Thread.currentThread());//哪个线程运行的这句代码,获取到的就是线程的对象
        System.out.println(Thread.currentThread().getName());//哪个线程运行的这句代码,获取到的就是线程的对象
    }

效果:

(2)其他线程名字都为Thread-0、Thread-1...等等。方法是直接在run方法中写super.getName()即可,其中super可以省略。也可以写currentThread方法

public class SubThread extends Thread{
    public void run(){
        System.out.println(super.getName());
        System.out.println(getName());
        System.out.println(Thread.currentThread());
        System.out.println(Thread.currentThread().getName());
    }
}

效果:

2. 线程名字的设置(setName方法)

(1)最好是在start方法上面先定义,逻辑上比较好理解。

public static void main(String[] args) {
        SubThread st = new SubThread();//创建一个继承了Thread类的对象
        st.setName("Thread-111");
        st.start();//调用start方法,是多线程程序
}

public class SubThread extends Thread{
    public void run(){
        System.out.println(super.getName());
        System.out.println(getName());
        System.out.println(Thread.currentThread());
        System.out.println(Thread.currentThread().getName());
    }
}

(2)子类中设置

public static void main(String[] args) {
        SubThread st = new SubThread();//创建一个继承了Thread类的对象
        st.start();//调用start方法,是多线程程序
}

public class SubThread extends Thread{

    public SubThread(){
        super("222");
    }

    public void run(){
        System.out.println(super.getName());
        System.out.println(getName());
        System.out.println(Thread.currentThread());
        System.out.println(Thread.currentThread().getName());
    }
}

效果:

 

 

Thread类的常用方法

 

 

Thread类的sleep方法

sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂时停止执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。

下面代码的效果:每隔1s输出一次。

public static void main(String[] args) throws InterruptedException {
    for(int i=0;i<10;i++){
        Thread.sleep(1000);//暂停1s
        System.out.println(i);
    }
}

如果是写在run方法里,由于不能够抛出异常,所以需要写try catch

public class SubThread extends Thread{
    public void run(){
        for(int i=0;i<10;i++){
            try {
                Thread.sleep(1000);//暂停1s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
        }
    }
}

 

匿名内部类实现线程程序

 

注意:

1. 匿名内部类的前提,必须有继承或者接口的实现。

2. 写法为new 父类或者接口(){重写抽象方法}

//匿名内部类的前提,必须有继承或者接口的实现
//new 父类或者接口(){
//  重写抽象方法
//}
public static void main(String[] args) throws InterruptedException {
    //写法一:继承方式 xxx extends Thread{public void run(){}}
    new Thread(){
        public void run(){
            System.out.println("1");
        }
    }.start();

    //写法二:实现接口方式  xxx implement Runnable{public void run(){}}
    Runnable r = new Runnable() {
        public void run() {
            System.out.println("2");
        }
    };
    new Thread(r).start();

    //写法三:
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("3");
        }
    }).start();
}

 

线程的状态

NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED
已退出的线程处于此状态。

 

参考:

1. 黑马程序员视频

持续更新!!!

  
 
 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!