概述
什么是进程,线程?进程和线程的区别?
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(进程,线程,并发,并行,多线程,创建多线程的方法,线程调度以及设置优先级,线程休眠,加入线程,守护线程,线程死亡,清除线程阻塞)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复