进程(Process)和线程(Thread)的有什么何区别
定义
-
进程是具有一定独立功能的的程序,进程是系统进行资源分配和调度的一个独立单元。
-
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源
区别
- 一个程序至少有一个进程,一个进程至少有一个线程.
- 线程的划分尺度小于进程,使得多线程程序的并发性高。
- 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
- 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是进程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。
普通方法调用和多线程
线程实现(重点)
继承Thread类(重点)
实现Runnable接口(重点)
实现Callable接口
线程的创建
1.继承Thread类(重点)
第一步:自定义线程类继承Thread类。
第二步:重写run()方法,编写线程执行体。
第三步:创建线程对象,调用start()方法启动线程。
package com.tjrac.demo01;
//第一步:自定义线程类继承Thread类。
//第二步:重写run()方法,编写线程执行体。
//第三步:创建线程对象,调用start()方法启动线程。方法启动线程
public class TestThread1 extends Thread{
@Override
public void run() {
for(int i=0;i<20;i++){
System.out.println("我在看代码");
}
}
public static void main(String[] args) {
//创建线程对象
TestThread1 testThread1=new TestThread1();
//调用对象的start方法
testThread1.start();
for(int i=0;i<200;i++){
System.out.println("我学习多线程"+i);
}
}
}
实战下载图片
package com.tjrac.demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//第一步:自定义线程类继承Thread类。
//第二步:重写run()方法,编写线程执行体。
//第三步:创建线程对象,调用start()方法启动线程。方法启动线程
public class TestThread1 extends Thread{
private String url;//网络图片的地址
private String name;//保存的文件名
public TestThread1(String url,String name){
this.url=url;
this.name=name;
}
//下载线程的执行体
@Override
public void run() {
WedDownlaoder wedDownlaoder= new WedDownlaoder();
wedDownlaoder.Downlaoder(url,name);
System.out.println(name+"下载完成");
}
public static void main(String[] args) {
//创建线程对象
TestThread1 t1=new TestThread1("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1577168219347&di=5bb4508ef618b0da3aed3d042eb8997e&imgtype=0&src=http%3A%2F%2Fimaegs.creditsailing.com%2Farticle%2F121%2F57_ua3ao__.jpg","陈声铭用多线程下载的图片01.jpg");
TestThread1 t2=new TestThread1("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1577168219347&di=5bb4508ef618b0da3aed3d042eb8997e&imgtype=0&src=http%3A%2F%2Fimaegs.creditsailing.com%2Farticle%2F121%2F57_ua3ao__.jpg","陈声铭用多线程下载的图片02.jpg");
TestThread1 t3=new TestThread1("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1577168219347&di=5bb4508ef618b0da3aed3d042eb8997e&imgtype=0&src=http%3A%2F%2Fimaegs.creditsailing.com%2Farticle%2F121%2F57_ua3ao__.jpg","陈声铭用多线程下载的图片03.jpg");
//调用对象的start方法
t1.start();
t2.start();
t3.start();
}
}
//下载器
class WedDownlaoder{
//下载方法
public void Downlaoder(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("Downlaoder方法异常");
}
}
}
总结:线程开启不一定执行,由CPU调度执行,不推荐用Thread类,因为要不免单继承的局限性。
2.实现Runnable接口(重点)
第一步:实现Runnable接口。
第二步:重写run()方法。
第三步:将线程执行的接口放入接口实现类。
package com.tjrac.demo01;
//第一步:实现Runnable接口。
//第二步:重写run()方法。
//第三步:将线程执行的接口放入接口实现类。
public class TestRunnable01 implements Runnable{
@Override
public void run() {
for(int i=0;i<20;i++){
System.out.println("下载完成");
}
}
public static void main(String[] args) {
//创建Runnable接口的实现类对象
TestRunnable01 testRunnable = new TestRunnable01();
//创建线程对象,通过线程对象来启动线程
new Thread(testRunnable).start();
for(int i=0;i<500;i++){
System.out.println("下载完成"+i);
}
}
}
实战下载图片(荣耀战魂)
package com.tjrac.demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestRunnable02 implements Runnable{
private String url;//网络图片的地址
private String name;//保存的文件名
public TestRunnable02(String url,String name){
this.url=url;
this.name=name;
}
//下载线程的执行体
@Override
public void run() {
RunnableDownlaoder runnableDownlaoder= new RunnableDownlaoder();
runnableDownlaoder.Downlaoder(url,name);
System.out.println(name+"下载完成");
}
public static void main(String[] args) {
//创建线程对象
TestRunnable02 t1=new TestRunnable02("https://imgsa.baidu.com/forum/pic/item/8a13632762d0f703b83556ae07fa513d2697c539.jpg","陈声铭用多线程下载的图片04.jpg");
//调用对象的start方法
new Thread(t1).start();
}
}
//下载器
class RunnableDownlaoder{
//下载方法
public void Downlaoder(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("Downlaoder方法异常");
}
}
}
总结:推挤使用Runnable接口,方便灵活,可以一个对象被多个线程使用,但是会出现多线程并发的问题。
多个线程同时操作一个对象实战
卖火车票
package com.tjrac.demo01;
//多个线程同时操作一个对象
//卖火车票
public class TestRunnable03 implements Runnable {
private int ticket=10;
@Override
public void run() {
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
if(ticket<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"买到了第"+ticket--+"票");
}
}
public static void main(String[] args) {
TestRunnable03 testRunnable03=new TestRunnable03();
new Thread(testRunnable03,"陈声铭").start();
new Thread(testRunnable03,"雷志明").start();
new Thread(testRunnable03,"唐可明").start();
new Thread(testRunnable03,"陈笑").start();
}
}
3.实现Callable接口
实战下载图片
package com.tjrac.demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class TestCallable01 implements Callable<Boolean> {
private String url;//网络图片的地址
private String name;//保存的文件名
public TestCallable01(String url,String name){
this.url=url;
this.name=name;
}
//下载线程的执行体
@Override
public Boolean call() {
RunnableDownlaoder runnableDownlaoder= new RunnableDownlaoder();
runnableDownlaoder.Downlaoder(url,name);
System.out.println(name+"下载完成");
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程对象
TestCallable01 t1=new TestCallable01("https://imgsa.baidu.com/forum/w%3D580%3B/sign=16a0044f35f33a879e6d0012f6671138/37d12f2eb9389b508c5b672d8a35e5dde7116e2d.jpg","陈声铭用多线程下载的图片05.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> r1=ser.submit(t1);
//获取结果
boolean rs1=r1.get();
//关闭服务器
ser.shutdownNow();
}
}
//下载器
class CallableDownlaoder{
//下载方法
public void Downlaoder(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("Downlaoder方法异常");
}
}
}
线程的5个状态
- new
- 就绪状态
- 运行状态
- 阻塞状态
- dead
停止线程
package com.kuang.springbootmybatis.controller;
public class TestRunnable03 implements Runnable {
public boolean flag=true;
@Override
public void run() {
int ticket=0;
while (flag) {
System.out.println("Thread...run"+ticket++);
}
}
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
TestRunnable03 testRunnable03=new TestRunnable03();
new Thread(testRunnable03).start();
for (int i=0;i<=10000;i++){
System.out.println("main"+i);
if(i==9000){
testRunnable03.stop();
System.out.println("线程停止");
}
}
}
}
线程休眠
- sleep指定当前线程阻塞速秒。
- sleep存在异常InterruptedException。
- sleep时间到达之后线程进入就绪状态。
- sleep可以用来模拟网络延时,倒计时等。
- 每一个对象都有一个锁,sleep不会释放锁。
package com.kuang;
import jdk.internal.dynalink.beans.StaticClass;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep {
public static void main(String[] args){
Date data=new Date(System.currentTimeMillis());
while(true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(data));
data=new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//倒计时
public static void tenDown() throws InterruptedException {
int num=10;
while(true){
Thread.sleep(1000);
System.out.println(num--);;
if(num<=0){
break;
}
}
}
}
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞。
- 将线程从运行状态转为就绪状态。
- 让cpu重新调度,礼让不一定成功!看CPU心情
package com.kuang;
public class TestYield {
public static void main(String[] args){
MyYield myYield=new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
//礼让
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程结束");
}
}
join
- join合并线程,待该线程执行完成后,再执行其他线程,其他线程阻塞。
- 就是插队