我是靠谱客的博主 现实盼望,最近开发中收集的这篇文章主要介绍Java基础知识 28(进程,线程,并发,并行,多线程,创建多线程的方法,线程调度以及设置优先级,线程休眠,加入线程,守护线程,线程死亡,清除线程阻塞),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

什么是进程,线程?进程和线程的区别?

1.进程

当一个程序进入内存运行时,即变成一个进程。进程是处于运行过程中的程序。进程是操作系统进行资源分配和调度的一个独立单位。进程是拥有资源的基本单位。我们现在的计算机是支持多进程的,可以运行多个进程。

进程的三个特征:

(1)独立性:独立存在的实体,每个进程都有自己独立私有的一块内存空间。

(2)动态性:程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。

(3)并发性:多个进程可在单处理器上并发执行。

并发性和并行性:

线程是并发的,进程是并行的。

并发是指在同一时间内有多个任务高速的交替执行(因为是高速切换,可能会觉得同时执行)。

并行是指应用能够同时执行不同的任务。

2.线程

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。线程也被称作轻量级进程,线程在进程中是独立,并发的执行流。线程是程序使用CPU的基本单位。我们的进程开启后,会执行很多的任务,那么每一个任务就称之为一个线程。

3.线程和进程的区别

(1)线程是进程的组成部分,线程要依赖于进程而存在,没有进程,也就谈不上线程。一个线程可以有多个进程,每个线程执行不同的任务。

(2)不同的进程使用不同的内存空间,而线程与父进程的其他线程共享父进程所拥有的全部资源。

(3)别把内存空间和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。线程拥有自己的堆栈,自己的程序计数器和自己的局部变量,不拥有系统资源。

(4)现成的调度和管理有进程本身负责完成。操作系统对进程进行调度,管理和资源分配。

多线程的优势

多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。我们的程序在执行的时候,都是在抢CPU的执行权,如果是多线程的程序,那么在抢到CPU的执行权的概率应该比单线程程序抢到的概率要大,也就是说,CPU在多线程程序中执行的时间要比单线程多,所以提高了程序的使用率,即便是多线程程序,他们中的哪个线程能抢到CPU的资源,这也是不确定的,所以多线程具有随机性。

问:我们的计算机在同一个时间点上,是同时执行多个文件吗?比如说QQ音乐,游戏,你在操作上会感觉到这两个进程在同时执行,我一边听歌一边玩游戏,是同时在运行的。

答:不是同时执行的,我们的(单核CPU)CPU在同一个时间点上只执行一个进程,你的感觉多个进程在同时执行,那是因为CPU在多个进程之间进行了一个高速的切换执行,你的人耳和眼睛根本感觉不到。

Java程序的运行原理

Java命令会启动Java虚拟机启动 JVM,等于启动了一个应用程序,也就是启动了一个进程,该进程会自动启动一个主线程,然后主线程去调用某个类的main方法,所以main方法运行在主线程中。

JVM的启动是多线程的吗?

JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

Java中多线程的实现方式:

1.继承Thread类

Java给我们提供好了一个类Thread,通过这个类,创建线程,开启线程。Thread线程是程序的执行线程,Java虚拟机允许应用程序并发的运行多个执行线程。

创建新线程的思路:

(1)将一个类声明为Thread的子类,也就是继承Thread。

(2)该子类应重写Thread的run方法。

(3)接下来可以分配并启动该子类的实例。

public class Mytest {
    public static void main(String[] args) {
        System.out.println("主线程中的代码执行了");
        System.out.println("主线程中的代码执行了");
        System.out.println("主线程中的代码执行了");
        System.out.println("主线程中的代码执行了");
        System.out.println("当主线程执行到这个节点的时候,可能会有一个比较耗时的操作,那么我们就在这个节点处开启一个子线程来执行耗时的代码");
        //创建一个线程
        myThread myThread = new myThread();
        //开启线程,调用run()方法,其实子线程并没有开启,这种方法其实就是你创建了一个类的对象,然后调用了类中的一个方法。
        //myThread.run();
        //正确开启线程的方式,调用start()方法,当线程开启后,由子线程调用run()方法来执行run()方法里面的代码
        //线程不要重复开启,重复开启线程会抛出异常。
        myThread.start();
        System.out.println("主线程后面的代码执行了");
        System.out.println("主线程后面的代码执行了");
        System.out.println("主线程后面的代码执行了");
        System.out.println("主线程后面的代码执行了");
        System.out.println("主线程执行到这里的时候,又遇到了一个耗时的操作,那么我们就在这个节点处再来一个子线程进行耗时的操纵");
        myThread myThread1 = new myThread();
        myThread1.start();
        System.out.println("直到这里,耗时的操作才算执行结束了");
        System.out.println("接下来执行主线程剩下的代码");
        System.out.println("接下来执行主线程剩下的代码");
        System.out.println("接下来执行主线程剩下的代码");
        System.out.println("接下来执行主线程剩下的代码");
    }
}
------------------------------------
public class myThread extends Thread {//子线程继承Thread
    @Override            //并重写run()方法
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(i);
        }
    }
}

Thread类中的start()和run()方法有什么区别?

(1)start()方法被用来启动新线程,而且start()方法内部源码调用了run()方法,这和直接调用run()方法不一样。

(2)当你调用run()方法的时候,只会在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

需要注意的是:不能对同一线程对象两次调用start()方法。

currentThread():获取当前执行的线程对象。

public class Mytest {
    public static void main(String[] args) {
        System.out.println("主线程");
        //currentThread();获取当前执行的线程对象
        Thread thread = Thread.currentThread();
        thread.setName("我是主线程");
        String name = thread.getName();
        System.out.println(name);

        myThread myThread1 = new myThread();
        myThread myThread2 = new myThread();
        myThread myThread3 = new myThread();
        //多个线程并发执行时(),多个抢占CPU的执行权,哪个线程抢到,在某一个时刻,就会执行哪个线程,线程的执行具有随机性。
        myThread1.setName("王源");
        myThread2.setName("王俊凯");
        myThread3.setName("易烊千玺");
        myThread1.start();
        myThread2.start();
        myThread3.start();
    }
}
--------------------------------------
public class myThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //this.getName()获得子线程的名字
            //System.out.println("子线程:"+this.getName()+":"+i);
            System.out.println("子线程:"+this.getName()+":"+i);
        }
    }
}

2.实现Runnable接口创建线程类

创建新线程的思路:

(1)定义一个类实现Runnable接口。

(2)重写接口中的run()方法。

(3)然后可以分配该类的实例。

(4)再创建Thread对象时作为一个参数来传递并启动。

这种方法的扩展性很强,实现一个接口,还可以再去继承其他的类。

Thread(Runnable target)分配新的Thread对象

MyRunnable myRunnable = new MyRunnable();
//Thread(Runnable target,String name)
//分配新的Thread对象
Thread th1 = new Thread(myRunnable, "A线程");
Thread th2 = new Thread(myRunnable, "B线程");
th1.start();
th2.start();

Runnable 任务 接口应该由那些打算通过某一线程执行其实例的类来实现。

public class Mytest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.setName("诸葛亮");
        thread.start();
        MyRunnable2 myRunnable2 = new MyRunnable2();
        Thread thread2 = new Thread(myRunnable2);
        thread2.setName("周瑜");
        thread2.start();
    }
}
------------------------------------
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"==="+i);
        }
    }
}
------------------------------------
public class MyRunnable2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"==="+i);
        }
    }
}

3.使用Callable和FutureTask创建线程

创建线程的思路:

(1)创建一个类实现Callable接口,重写接口中的call方法。

(2)创建一个FutureTask类将Callable的子类对象作为参数传递进去。

(3)创建Thread类,将FutureTask对象作为参数传递进去。

(4)开启线程。

Runnable 任务 让线程来执行run() 没有返回值 这个方法不能抛出异常,只能抓
Callable 任务 让线程来执行call() 有返回值 可以抛出异常

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Mytest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*
         * 创建线程的第三种方式:
         * 1.创建一个类实现Callable接口,重写接口中的call方法
         * 2.创建一个FutureTask类将Callable的子类对象作为参数传进去
         * 3.创建Thread类,将FutureTask对象作为参数传进去
         * 4.开启线程
         */
        //我们有一种需求,当子线程执行完后,我想获取子线程执行完之后的结果。
        MyCallable myCallable = new MyCallable(100);
        FutureTask<Integer> task = new FutureTask<>(myCallable);
        Thread thread = new Thread(task);
        thread.start();
        //获取线程执行完之后返回的结果
        Integer integer = task.get();
        System.out.println(integer);

        MyCallable myCallable2 = new MyCallable(1000);
        FutureTask<Integer> task2 = new FutureTask<>(myCallable2);
        Thread thread2 = new Thread(task2);
        thread2.start();

        Integer integer2 = task2.get();
        System.out.println(integer2);

        //Runnable 任务 让线程来执行run() 没有返回值 这个方法不能抛出异常,只能抓
        //Callable 任务 让线程来执行call() 有返回值  可以抛出异常
    }
}
----------------------------------------
import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    private final int num;

    public MyCallable(int num) {
        this.num=num;
    }

    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 0; i < num; i++) {
            sum+=i;
        }
        return sum;
    }
}

设置线程优先级以及线程的调度:Priority

线程的执行:假如我们的计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU的时间片,也就是执行权,才可以执行指令,那么Java是如何对线程进行调度的呢?

线程调度有两种调度模型:

(1)分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。

(2)抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么就随机选择一个。优先级高的线程获取的CPU时间片相对多一些。Java使用的就是抢占式调度模型。

public class Mytest {
    public static void main(String[] args) {
        /*
         *线程的执行:
         * 假如我们的计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,
         * 线程只有得到CPU的时间片,也就是行使权,才可以执行指令,那么Java是如何对线程进行调用的呢?
         *
         * 线程的调用有两种调度模型:
         * 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。
         * 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么就随机选择一个。
         * 优先级高的线程获取的CPU时间片相对多一些
         * Java使用的就是抢占式调度模型。
         */
        MyThread th1 = new MyThread();
        MyThread th2 = new MyThread();
        MyThread th3 = new MyThread();
        //设置优先级:范围是1--10 线程默认的优先级是5
        //Thread.MAX_PRIORITY;   10
        //Thread.MIN_PRIORITY;    1
        //Thread.NORM_PRIORITY    5
        th1.setPriority(1);
        th2.setPriority(2);
        th3.setPriority(Thread.MAX_PRIORITY);
        //多个线程并发执行,多个抢占CPU的执行权,哪个线程抢到,在某一个时刻,就会执行某个线程。线程的执行具有随机性。
        //获取线程的优先级
        int priority1 = th1.getPriority();
        int priority2 = th1.getPriority();
        int priority3 = th1.getPriority();

        System.out.println("线程的优先级"+priority1);
        System.out.println("线程的优先级"+priority2);
        System.out.println("线程的优先级"+priority3);
        th1.setName("王源");
        th2.setName("王俊凯");
        th3.setName("易烊千玺");
        th1.start();
        th2.start();
        th3.start();
    }
}
------------------------------------
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("子线程:"+this.getName()+":"+i);
        }
    }
}

线程控制之休眠线程:sleep,线程礼让(yield)

yield可以直接用Thread类调用,可以让当前正在执行的线程暂停,不会阻塞该线程,只是将该线程转入就绪状态。yield让出CPU执行权给同等级的线程,如果没有相同级别的线程在等待CPU的执行权,则该线程继续执行。

二者的区别:

(1)sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给优先级相同,或优先级更高的线程执行机会。
(2)sleep()方法会将线程转入阻塞状态(block状态),直到经过阻塞时间才会转入就绪状态;而yield()方法不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。 因此完全有可能某个线程调用yield()方法暂停之后,立即再次获得处理器资源被执行。
(3)sleep()方法声明抛出了InterruptedException异常,所以调用sleep()方法时要么捕捉该异常,要么显式声明抛出该异常;而yield()方法则没有声明抛出任何异常。
(4)sleep()方法比yield()方法有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行。

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Thread.yield();//线程礼让
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
---------------------------------------
public class Mytest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程开始执行了");
        Thread.sleep(2000);
        System.out.println("主线程下面的代码");
        //线程休眠:可以让当前正在执行的线程休息一会
        //Thread(String name) 分配新的Thread对象
        //通过有参构造,可以给线程起个名字
        MyThread myThread1 = new MyThread();
        myThread1.setName("刘备");
        myThread1.start();
        MyThread myThread2 = new MyThread();
        myThread2.setName("关羽");
        myThread2.start();
        MyThread myThread3 = new MyThread();
        myThread3.setName("张飞");
        myThread3.start();
    }
}
--------------------------------------
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            try {
                Thread.sleep(1000);//单位是毫秒
                System.out.println(this.getName()+i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

加入线程(join)

等待该线程执行完毕之后,其他线程才能再次执行。注意:在线程启动之后,再调用方法。

join()可以让多个线程并发执行,变成串行(挨个排队执行,不用抢)

public class Mytest {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread1 = new MyThread();
        myThread1.setName("刘备");
        myThread1.start();
        //注意:join在线程启动之后执行
        myThread1.join();
        MyThread myThread2 = new MyThread();
        myThread2.setName("关羽");
        myThread2.start();
        myThread2.join();
        MyThread myThread3 = new MyThread();
        myThread3.setName("张飞");
        myThread3.start();
        myThread3.join();
    }
}
-------------------------------
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);//单位是毫秒
                System.out.println(this.getName()+i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

守护线程(setDaemon)

Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) 。
用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为没有如果没有了被守护,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,VM就不会退出。
守护线程的特征:如果所有前台线程都死亡,后台线程会自动死亡。
守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的
setDaemon(true)方法设置当前线程为守护线程。

虽然守护线程可能非常有用,但必须小心确保其他所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。 因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。

1、setDaemon(true)必须在调用线程的start()方法之前设置,否则会跑出IllegalThreadStateException异常。
2、在守护线程中产生的新线程也是守护线程。  
3、 不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。 
public class Mytest {
    public static void main(String[] args) {
        Thread.currentThread().setName("刘备");
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
        System.out.println("------------------");
        MyThread th1 = new MyThread();
        MyThread th2 = new MyThread();
        th1.setName("关羽");
        th2.setName("张飞");
        //设置守护线程 当主线程死亡后,守护线程也要马上死亡
        //注意:setDaemon(true) 该方法必须在启动线程之前
        th1.setDaemon(true);
        th2.setDaemon(true);
        th1.start();
        th2.start();
    }
}

线程死亡(stop),清除线程阻塞状态(interrupt)

public class Mytest {
    public static void main(String[] args) throws InterruptedException {
        MyThread th1 = new MyThread();
        th1.setName("杨超越");
        th1.start();
        Thread.sleep(2000);
        //让线程死亡
        //th1.stop();
        //清除线程的阻塞状态
        th1.interrupt();
    }
}
---------------------------------------------
public class MyThread extends Thread{
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"==="+i);
        }
    }
}

最后

以上就是现实盼望为你收集整理的Java基础知识 28(进程,线程,并发,并行,多线程,创建多线程的方法,线程调度以及设置优先级,线程休眠,加入线程,守护线程,线程死亡,清除线程阻塞)的全部内容,希望文章能够帮你解决Java基础知识 28(进程,线程,并发,并行,多线程,创建多线程的方法,线程调度以及设置优先级,线程休眠,加入线程,守护线程,线程死亡,清除线程阻塞)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部