大家好我是wave,本篇文章给大家介绍一些有关线程的基本概念与线程的一些基本操作

线程与进程

  • 进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。还存在资源开销、包含关系、内存分配、影响关系、执行过程等区别。同一进程的线程共享本进程的地址空间和资源(堆和方法区),而进程之间的地址空间和资源相互独立。
  • 举个栗子就是我们从桌面随便打开一个应用程序,比如开启一个qq或者wegame,都算是启动了一个进程。而线程是一个比进程更小的单位,比如在qq打开多个聊天窗口与别人聊天,就是一种多线程的表现。

并发与并行

  • 在计算机的早期cpu是只有一个核的,也就是说只能同时执行一个任务,但是我们仍然可以在电脑上同时运行很多的软件,这是为什么呢?其实这就涉及到一个概念叫并发。

并发:指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程或线程快速交替的执行。

  • 随着计算机发展,现在的cpu基本都是多核的了,所有也有一个并行的概念。也就是不同的任务由不同的核来执行,就可以产生并行的情况。
  • 为什么要使用多线程呢?
  • 如我们要计算一个复杂的任务,我们只用一个线程的话,CPU 只会一个 CPU 核心被利用到,而创建多个线程就可以让多个 CPU 核心被利用到,这样就提高了 CPU 的利用率。
  • 那么要如何启动一个线程呢?

Thread

如果一个类继承了Thread类,那么这个类就可以看成是一个线程,调用这个类的start方法,就可以启动线程。但是要记得一定要重写这个Thread类中的run方法,这个线程的业务逻辑就应该写在run方法中,start方法被调用后就会运行run方法里面的业务逻辑。

演示一个非线程的类

//演示一个非线程的类
class Hero{public String name;//名字public int hp;//生命值public int damage;//伤害Hero(String name,int hp,int damage){this.name = name;this.hp = hp;this.damage = damage;}Hero(){}public void attack(Hero hero){try {//方便查看攻击过程Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//攻击动作hero.hp -= this.damage;System.out.println(this.name + "正在攻击" + hero.name + "," + hero.name + "当前生命值为" + hero.hp);if(hero.isDead()){System.out.println(hero.name + "已经死了");}}public boolean isDead(){return hp < 0 ? true : false;}
}
public class Solution {public static void main(String[] args) {Hero Darius = new Hero();Darius.name = "德莱厄斯";Darius.hp = 616;Darius.damage = 50;Hero Anivia = new Hero();Anivia.name = "艾尼维亚";Anivia.hp = 300;Anivia.damage = 30;Hero Draven = new Hero();Draven.name = "德莱文";Draven.hp = 500;Draven.damage = 65;Hero leesin = new Hero();leesin.name = "盲仔";leesin.hp = 455;leesin.damage = 80;//诺手攻击冰鸟while(!Anivia.isDead()){Darius.attack(Anivia);}//德莱文攻击盲僧while(!leesin.isDead()){Draven.attack(leesin);}}
}

  • 我们可以看到代码是顺序执行的,所以必须先执行完诺手攻击冰鸟才能进行德莱文攻击盲僧。那在真实场景下肯定不是这样的呀,诺手的攻击和德莱文的攻击应该是同步进行的,那使用线程来优化一下吧~
class Hero{public String name;//名字public int hp;//生命值public int damage;//伤害Hero(String name,int hp,int damage){this.name = name;this.hp = hp;this.damage = damage;}Hero(){}public void attack(Hero hero){try {//方便查看攻击过程Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//攻击动作hero.hp -= this.damage;System.out.println(this.name + "正在攻击" + hero.name + "," + hero.name + "当前生命值为" + hero.hp);if(hero.isDead()){System.out.println(hero.name + "已经死了");}}public boolean isDead(){return hp < 0 ? true : false;}
}
class Kill extends Thread{private Hero hero1;private Hero hero2;public Kill(Hero h1, Hero h2){this.hero1 = h1;this.hero2 = h2;}//重写run方法public void run(){while(!hero2.isDead()){hero1.attack(hero2);}}
}
public class Solution {public static void main(String[] args) {Hero Darius = new Hero();Darius.name = "德莱厄斯";Darius.hp = 616;Darius.damage = 50;Hero Anivia = new Hero();Anivia.name = "艾尼维亚";Anivia.hp = 300;Anivia.damage = 30;Hero Draven = new Hero();Draven.name = "德莱文";Draven.hp = 500;Draven.damage = 65;Hero leesin = new Hero();leesin.name = "盲仔";leesin.hp = 455;leesin.damage = 80;Kill killThread1 = new Kill(Darius,Anivia);killThread1.start();Kill killThread2 = new Kill(Draven,leesin);killThread2.start();}
}

  • 这里使用了Kill类,让英雄的攻击操作是在不同的线程完成的,这样就可以达到德莱厄斯和德莱文的攻击是同时进行的了,而不是先执行写在前面的代码,再执行写在后面的代码。

Runnable

Runnable演示案例

//演示Runnable
class Hero{public String name;//名字public int hp;//生命值public int damage;//伤害Hero(String name,int hp,int damage){this.name = name;this.hp = hp;this.damage = damage;}Hero(){}public void attack(Hero hero){try {//方便查看攻击过程Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//攻击动作hero.hp -= this.damage;System.out.println(this.name + "正在攻击" + hero.name + "," + hero.name + "当前生命值为" + hero.hp);if(hero.isDead()){System.out.println(hero.name + "已经死了");}}public boolean isDead(){return hp < 0 ? true : false;}
}
class Kill implements Runnable{private Hero hero1;private Hero hero2;public Kill(Hero h1, Hero h2){this.hero1 = h1;this.hero2 = h2;}//重写run方法public void run(){while(!hero2.isDead()){hero1.attack(hero2);}}
}
public class Solution {public static void main(String[] args) {Hero Darius = new Hero();Darius.name = "德莱厄斯";Darius.hp = 616;Darius.damage = 50;Hero Anivia = new Hero();Anivia.name = "艾尼维亚";Anivia.hp = 300;Anivia.damage = 30;Hero Draven = new Hero();Draven.name = "德莱文";Draven.hp = 500;Draven.damage = 65;Hero leesin = new Hero();leesin.name = "盲仔";leesin.hp = 455;leesin.damage = 80;Kill killThread1 = new Kill(Darius,Anivia);//新建一个线程类,把KillThread1作为参数传入//因为实现Runnable接口并没有start方法,所以需要一个Thread类才能启动线程new Thread(killThread1).start();Kill killThread2 = new Kill(Draven,leesin);new Thread(killThread2).start();}
}

  • 仔细对比Thread与Runnable各自实现一个线程的区别,你会发现Kill类上除了把继承Thread类改成了实现Runnable类之外并没有改变。有改变的地方是main函数中现实Runnable接口的Kill类需要当做一个参数传入Thread类中,因为只实现了Runnable接口并没有start方法,也就是不能作为线程的启动类。
  • 打开Thread与Runnable的源码,你也会发现Runnable只定义了一个run方法。而Thread类中就有大量的方法,有些方法是native关键字修饰的,所以启动一个线程必须是一个Thread类。这也是实现了Runnable接口的类还需要当做参数传入Thread类的愿因。

Thread类源码片段

public
class Thread implements Runnable {/* Make sure registerNatives is the first thing <clinit> does. */private static native void registerNatives();static {registerNatives();}

Runnable接口源码

@FunctionalInterface
public interface Runnable {/*** When an object implementing interface <code>Runnable</code> is used* to create a thread, starting the thread causes the object's* <code>run</code> method to be called in that separately executing* thread.* <p>* The general contract of the method <code>run</code> is that it may* take any action whatsoever.** @see     java.lang.Thread#run()*/public abstract void run();
}
  • 如果你对Runnable里面为啥有抽象方法不解的话就仔细阅读下面文字。
  • 函数式接口,首先是一个接口,然后就是在这个接口里面只能有一个抽象方法,但是可以有多个非抽象方法的接口。
  • Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
  • 函数式接口可以被隐式转换为 lambda 表达式。

lambda表达式创建一个线程

//lambda表达式创建一个线程public static void main(String[] args) {new Thread(()->{int n = 0;for (int i = 0; i < 10; i++) {n++;}System.out.println(n);//输出10}).start();}

有关Java8的知识后面会单独出一篇文章给大家介绍~

Callable

Callable使用案例

class Hero{public String name;//名字public int hp;//生命值public int damage;//伤害Hero(String name,int hp,int damage){this.name = name;this.hp = hp;this.damage = damage;}Hero(){}public void attack(Hero hero){try {//方便查看攻击过程Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//攻击动作hero.hp -= this.damage;System.out.println(this.name + "正在攻击" + hero.name + "," + hero.name + "当前生命值为" + hero.hp);if(hero.isDead()){System.out.println(hero.name + "已经死了");}}public boolean isDead(){return hp < 0 ? true : false;}
}
class Kill implements Callable{private Hero hero1;private Hero hero2;public Kill(Hero h1, Hero h2){this.hero1 = h1;this.hero2 = h2;}@Overridepublic Object call() throws Exception {while(!hero2.isDead()){hero1.attack(hero2);}//比Runnable多一个返回值return "战斗结束";}
}
public class Solution {public static void main(String[] args)throws Exception {Hero Darius = new Hero();Darius.name = "德莱厄斯";Darius.hp = 616;Darius.damage = 50;Hero Anivia = new Hero();Anivia.name = "艾尼维亚";Anivia.hp = 300;Anivia.damage = 30;Hero Draven = new Hero();Draven.name = "德莱文";Draven.hp = 500;Draven.damage = 65;Hero leesin = new Hero();leesin.name = "盲仔";leesin.hp = 455;leesin.damage = 80;//创建一个callableKill killThread1 = new Kill(Darius,Anivia);//把callable放入futureTaskFutureTask futureTask1 = new FutureTask(killThread1);//把futureTast放入Thread类,再启动线程new Thread(futureTask1).start();Kill killThread2 = new Kill(Draven,leesin);FutureTask futureTask2 = new FutureTask(killThread2);new Thread(futureTask2).start();//获取运行结果System.out.println(futureTask1.get());System.out.println(futureTask2.get());}
}

  • 除了Thread与Runnable之外Java还有一种创建线程的方式,就是实现Callable接口,再把Callable接口放入FutureTask类中,因为FutureTask实现了Runnable接口,然后就可以用Thread类启动线程了。
  • 对比Callable的代码与Runnable的代码其实也没有大多的改动,仅仅是把Run方法改成了Call方法,然后多一个返回值。
  • 使用Callable的好处就在于Thread类与Runnable接口实现一个线程是没有返回值的,也就是这个线程运行之后的结果需要使用共享变量或线程的通信,所以比较不方便。Callable的话就可以直接获取返回值。
  • Callable在很多地方都是配合线程池一起使用的,后序线程池的文章也会再和大家讲解的。

本次文章到这里就结束了,如果你是新手小白的话,建议运行运行文章中的代码,可以更直观的感受线程是什么样的。

Thread、Runnable、Callable基本操作线程基本概念相关推荐

  1. 多线程 -Thread -Runnable -Callable

    多线程(Thread) 线程简介 线程实现 线程状态 线程同步 线程通信问题 高级主题 核心概念 线程就是独立的执行路径; 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc程; ...

  2. 线程的创建与启动——Thread 类有两个常用的构造方法:Thread()与 Thread(Runnable)||多线程运行结果是随机的

    线程的创建与启动 在 Java 中,创建一个线程就是创建一个 Thread 类(子类)的对象(实例). Thread 类有两个常用的构造方法:Thread()与 Thread(Runnable).对应 ...

  3. 线程Thread,Runnable

    package com.aaa;//public class Ticket extends Thread{ //1.当主线程发现这个类的对象继承了Thread后,就会开一个新的线程让其执行 publi ...

  4. Java中如何使用Thread和Runnable创建的线程

    前言 我们都知道通过继承Thread和实现Runnable接口都能创建线程,那么他们有什么区别呢? 继承Thread创建线程 通过extends关键字继承Thread然后覆盖其run方法就可以实现一个 ...

  5. java基础 通过继承Thread类和实现Runnable接口创建线程

    java 创建线程 Java中,线程也是一种对象,但不是任何对象都可以成为线程. 只有实现了Runnable接口或继承了Thread类的对象才能成为线程. 继承Thread类 //格式: class ...

  6. Java多线程基础学习,Thread解读、java线程的状态、同步和异步、两阶段终止模式

    理论概述 单线程和多线程 为什么要使用多线程呢?多线程有什么好处呢? 如果在程序中,需要读写一个文件,该文件很大,那我们执行到该io操作时,cpu就会等待该io操作执行完才会继续运行下面的代码,进程调 ...

  7. java---多线程及线程的概念

    如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...

  8. JUC(3)List、Set、Map集合线程安全Callable创建线程三大工具类:CountDownLatch减法计数器、CyclicBarrier加法计数器、Semaphore计数信号量

    1. List集合线程安全 CopyOnWriteArrayList是线程安全的集合: ArrayList是线程不安全的集合: Vector是线程安全的集合(不推荐使用) 1.1 解决ArrarLis ...

  9. Java多线程-线程的概念和创建

    前言 声明:该文章中所有测试都是在JDK1.8的环境下. 该文章是我在学习Java中的多线程这方面知识时,做的一些总结和记录. 如果有不正确的地方请大家多多包涵并作出指点,谢谢! 一.基础概念 我们知 ...

最新文章

  1. python编程基础之三十三
  2. OpenGL中的混合(Blending)
  3. java 禁止使用多线程_Java多线程(四)-线程状态的转换 - Java 技术驿站-Java 技术驿站...
  4. JS:2.1,流程控制(if,switch)高级
  5. Linux内核源码目录
  6. Github上最热门的11个Java开源项目你会了吗
  7. 分类评价指标(二分类)——f1score sensitivity specificity roc曲线 auc
  8. 80psi等于多少kpa_【图】关于胎压的换算psi、bar,kpa
  9. 微信小程序图片裁剪image-cropper插件使用
  10. android apk下载完成后调用安装
  11. 小程序学习:自定义组件
  12. 门徒Disciples体系:致力于成为“DAO世界”中的集大成者。
  13. sql语句的各种模糊查询
  14. 【3】IMU模块:PA-IMU-460 ROS驱动 + 与GNSS时间同步
  15. 个人ACM模板(待持续补充)
  16. 一个体育生的编程之路
  17. java/mysql多个字段in从而保持字段一一对应
  18. 金融信贷风控实战(二)
  19. docx格式转doc格式时公式问题——MathType的使用
  20. JAVA简介及其编码规范

热门文章

  1. Nginx安装及简介
  2. 软件测试课程设计——智云云盘
  3. 工程线图中计算机的处理方法,CAD建筑施工图绘制复杂施工放线方法
  4. python使用opencv实现文档扫描并提取文字
  5. 在线免费一键将头像转换卡通形象
  6. The slave I/O thread stops because master and slave have equal MySQL server UUID
  7. 主从复制报错Fatal error:The slave I/O thread stops because master and slave have equal MySQL server UUIDs;
  8. 搭建Mysql双主双从报错The slave I/O thread stops because master and slave have equal MySQL server UUIDs(已解决)
  9. iPhone下mp4视频无法播放和部分手机只有声音没有画面
  10. Linux下硬盘加密