线程与进程
        线程与进程的区别
        
        最早的的时候DOS 系统有一个特点:只要电脑有病毒,那么电脑就死机了,是因为传统的DOS 系统属于单进程的操作系统
        即:在同一个时间段内只允许有一个程序运行。
        而后来到了window 时代发生了改变,电脑即使有病毒了也可以照常使用,但是会变慢
        因为在一个CPU ,一块资源的情况下,程序利用一些轮转算法,可以让一个资源在一个时间段上可以同时处理多个不同的程序(进程),但是i在一个时间点上只允许有一个进程去执行
        在每个进程上可以继续划分出若干个线程,那么线程的操作一定是要比进程更快的,所以多线程的操作性能一定要超过多进程的操作
        但是所有的线程都一定是要在进程的基础上进行划分,所以进程一旦消失,那么线程也会消失
        
    总结
        线程永远要依附于进程存在

*/

/*    多线程的实现(继承 Thread 类实现)
        实现javac的多线程操作
        
        在java中对于多线程实现一定要有一个线程的主类,而这个线程的主类往往是需要操作一些资源
        但是对于这个多线程主类的实现是有一定的要求:
            继承 Thread 父类
            实现 Runnable 接口( Callable 接口)
    
    继承 Thread 类实现多线程
        在 java.lang 包中存在有 Thread 类。子类继承 Thread 类之后需要覆写 Thread 类中的 run() 方法
        那么这个方法就属于线程的主方法,定义: public void run()
        
        范例:实现线程的主体类

class MyThread extends Thread{
private String name;
public MyThread(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println(this.name + ",i = "+i);
}
}
}

在线程的主类之中只是将内容输出10次
        但是需要注意的是:所有的多线程的执行一定是i并发完成的,即:在同一个时间段会有多个线程交替执行
        所以为了达到这样的目的,绝对不能够直接去调用 run() 方法,而是应该调用 Thread 类中的 start() 方法启动多线程:public void start()
        
        范例:启动多线程

MyThread mt1 = new MyThread("线程A");
MyThread mt2 = new MyThread("线程B");
MyThread mt3 = new MyThread("线程C");
mt1.start();
mt2.start();
mt3.start();

所有的线程都属于交替执行,本身是没有固定的执行顺序的
            
        思考:为什么现在启动多线程不使用 run() 方法。而非要使用 start() 方法?
            为了方便解释此问题,,必须打开 Thread 类中的 start() 源代码来观察

public synchronized void start() {

    if (threadStatus != 0)
throw new IllegalThreadStateException(); group.add(this); boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) { }
}
} private native void start0();

现在的代码之中首先可以发现方法会抛出一个异常: IllegalThreadStateException
            但是整个方法里面没有使用 throws 声明,没有 try...catch 捕获处理,而之所以会出现这样的情况是因为此异常属于 RuntimeException 的子类
            java.lang.Object
                java.lang.Throwable
                    java.lang.Exception
                        java.lang.RuntimeException
                            java.lang.IllegalArgumentException
                                java.lang.IllegalThreadStateException
                                
            此异常指的是一个线程已经调用了 start() 方法后uyou重复执行了 start() 方法所造成的问题
            在调用 start() 方法里面发现会调用 start0() 方法,而 start0() 方法上使用了 native 关键字定义,这个关键字指的是要调用本机的操作系统函数
            由于线程的启动需要牵扯到操作系统中的资源分配问题,所以具体的线程的启动应该要根据不同的操作系统有不同的实现
            而JVM相当于根据系统中定义的 start0() 方法来个根据不同的操作系统进行该方法的实现,这样在多线程的层次上 start0() 方法名称不改变
            而不同的操作系统上有不同的实现
            
            结论:只有 Thread 类的 start() 方法才能进行操作系统的资源的分配,所以启动多线程的方式永远就是调用 Thread 类的 start() 方法实现

package cn.mysterious.study3;

class MyThread extends Thread{
private String name;
public MyThread(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println(this.name + ",i = "+i);
}
}
} public class StudyThread { public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread mt1 = new MyThread("线程A");
MyThread mt2 = new MyThread("线程B");
MyThread mt3 = new MyThread("线程C");
mt1.start();
mt2.start();
mt3.start();
// 下面是 现执行A 后B 再C
mt1.run();
mt2.run();
mt3.run();
} }

实现 Runnable 接口
        继承 Thread 类会产生单继承的局限操作,所以现在最好的做法是利用接口来解决问题,于是就可以使用 Runnable 接口来完成操作
        首先来观察一下 Runnable 接口的定义结构:

@FunctionalInterface
public interface Runnable{
public void run();
}

此时的代码使用的是函数式的接口。可以利用 Lamda 表达式完成
        
        范例:按照正常思路实现多线程

class MyThread implements Runnable {
private String name;
public MyThread(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println(this.name + ",i = "+i);
}
}
}

如果要想启动多线程依靠只能够是 Thread 类中的 start() 方法,在之前继承 Thread 类的时候可以直接将 start() 方法继承下来继承使用
        但是现在实现的是 Runnable 接口,所以此方法没有了
        于是来观察 Thread 类中的构造方法: public Thread(Runnable target)

// TODO Auto-generated method stub
MyThread mt1 = new MyThread("线程A");
MyThread mt2 = new MyThread("线程B");
MyThread mt3 = new MyThread("线程C");
new Thread(mt1).start();
new Thread(mt2).start();
new Thread(mt3).start();

很多时候为了方便实现,可能直接使用匿名内部类或者是 Lamda 实现代码
        
        范例:观察实现

public class StudyThread {

    public static void main(String[] args) {
String name = "?????";
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println(name + ",i = "+i);
}
}
}).start();
} }

范例:JDK1.8使用 Lamda

public class StudyThread {

    public static void main(String[] args) {
String name = "?????";
new Thread(()->{ for (int i = 0; i < 10; i++) {
System.out.println(name + ",i = "+i);
}
}
).start();
} }

只要给出的是函数式接口基本上就都可以使用 Lamda 表达式或者是方法引用
        
    两种实现方式的区别(面试题)
        对于多线程的两种实现模式:继承 Thread 类,实现 Runnable 接口,那么这两种模式本质上来讲,一定使用 Runnable接口实现
        这样可以避免单继承局限,但是除了这样的使用原则之外,还需要清楚这两种实现方式的联系
        首先观察 Thread 类的定义结构:    
            public class Thread extends Object implements Runnable
        可以发现 Thread 类实现了 Runnable接口
        通过分析可以发现,整个代码的操作中使用的就是一个代理设计模式的结构,但是与传统的代理设计还有些差别
        如果按照传统的代理设计模式来讲,现在如果要想启动多线程理论应该是 run() 方法,但是实质上现在调用的是 start() 名称不符合
        之所以会这样主要是因为长期发展后的产物,最早的时候设计模式就是个梦
        除了以上的继承关联之外还有一点区别: Runnable 接口实现的多线程要比 Thread 类实现的多线程更方便的表示出数据共享的概念
        
        范例:希望有三个线程进行卖票 -- Thread 实现

package cn.mysterious.study3;

class MyThread extends Thread {
private int ticket = 5;
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
if (this.ticket > 0) {
System.out.println("卖票,ticket = "+ this.ticket --);
}
}
}
} public class StudyThread { public static void main(String[] args) {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
mt1.start();
mt2.start();
mt3.start();
} }
/*
结果:
卖票,ticket = 5
卖票,ticket = 4
卖票,ticket = 3
卖票,ticket = 2
卖票,ticket = 1
卖票,ticket = 5
卖票,ticket = 4
卖票,ticket = 3
卖票,ticket = 2
卖票,ticket = 1
卖票,ticket = 5
卖票,ticket = 4
卖票,ticket = 3
卖票,ticket = 2
卖票,ticket = 1

*/
            
            发现现在的三个线程各自都在卖着各自的票
            
        范例:使用 Runnable 接口来实现多线程

package cn.mysterious.study3;

class MyThread implements Runnable {
private int ticket = 5;
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
if (this.ticket > 0) {
System.out.println("卖票,ticket = "+ this.ticket --);
}
}
}
} public class StudyThread { public static void main(String[] args) {
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start(); } }

面试题:请解释多线程的两种实现方式以及区别?请分别用代码验证
            多线程需要一个线程的主类,这个类要么继承 Thread 类,要么实现 Runnable 接口
            使用 Runnable 接口可以比 Thread 类更好的实现数据共享的操作,并且利用 Runnable 接口可以避免单继承局限问题

    实现 Callable 接口
        从JDK1.5之后对于多线程的实现多了一个 Callable 接口,在这个接口里面比 Runnable 接口唯一的强大之处在于它可以返回执行结果
        此接口定义在 java.util.concurrent 包中

            @FunctionalInterface
public interface Callable<V>{
public V call() throws Exception
}

这个泛型表示的是返回值类型。call() 方法就相当于 run() 方法
        
        范例:定义线程的主题类

class MyThread implements Callable<String> {
private int ticket = 5;
@Override
public String call() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
if (this.ticket > 0) {
System.out.println("卖票,ticket = "+ this.ticket --);
}
}
return "票卖完了";
} }

但是现在出现了一个问题, Thread 类中并没有提供接收 Callable 接口的对象操作
            所现在如何启动多线程就出现了问题。为了分析出启动的操作,需要来观察继承结构
                首先来观察 java.util.concurrent Class FutureTask<V> 类的定义结构

public class StudyThread {

    public static void main(String[] args) throws Exception {
Callable<String> cal = new MyThread();
FutureTask<String> task = new FutureTask<>(cal); // 取得执行结果
Thread thread = new Thread(task);
thread.start();
System.out.println(task.get()); // 取得线程主方法的返回值
} }

对于线程的第三种实现方式没有特别的要求
            
    总结
        Thread 有单继承局限所以不使用,但是所有的线程对象一定要通过 Thread 里中的 start() 方法启动
        Runnable 使用是 可以避免单继承局限,所以建议使用此操作
        Callable 比 Runnable 唯一的好处是多了返回值的数据

菜鸡的Java笔记 第三十七 - java 线程与进程的更多相关文章

  1. 菜鸡的Java笔记 第三十三 - java 泛型

    泛型 GenericParadigm        1.泛型的产生动机        2.泛型的使用以及通配符        3.泛型方法的使用                JDK1.5 后的三大主 ...

  2. 菜鸡的Java笔记 第三十 - java 异常的捕获及处理

    异常的捕获及处理        1.异常的产生分析以及所带来的影响        2.异常的处理的基本格式        3.异常的处理流程        4.异常的处理模式        5.自定义 ...

  3. Java从零开始学三十七(JAVA IO- 二进制文件读写)

    一.简述 二.使用步骤 DataInputStream: 引入相关的类 构造数据输入流的对象(如:FileInputStream,BufferedWriter包装后读取文件等) 读取二进制文件的数据 ...

  4. Java进阶(三十七)java 自动装箱与拆箱

    Java进阶(三十七)java 自动装箱与拆箱 前言 这个是jdk1.5以后才引入的新的内容.java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装称为装箱,解包装称为 ...

  5. “全栈2019”Java多线程第三十七章:如何让等待的线程无法被中断

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. 【Java 线程的深入研究1】Java 提供了三种创建线程的方法

    Java 提供了三种创建线程的方法: 通过实现 Runnable 接口: 通过继承 Thread 类本身: 通过 Callable 和 Future 创建线程. 1.通过实现 Runnable 接口来 ...

  7. 菜鸡的Java笔记 第三十六 - java 函数式编程

    StudyLambda    Lambda 指的是函数式编程,现在最为流行的编程模式为面向对象,很多的开发者并不认可面向对象,所以很多的开发者宁愿继续使用 C 语言进行开发,也不愿意使用java,c+ ...

  8. Java开发笔记(三十七)利用正则串分割字符串

    前面介绍了处理字符串的常用方法,还有一种分割字符串的场景也很常见,也就是按照某个规则将字符串切割为若干子串.分割规则通常是指定某个分隔符,根据字符串内部的分隔符将字符串进行分割,例如逗号.空格等等都可 ...

  9. 菜鸡的Java笔记 第二十八 - java 包的定义

    包的主要作用以及定义    包的导入操作    系统常见的开发包    jar 程序命令        包的定义        在任何的操作系统之中都有一个统一的共识:同一个目录下不能够存在有相同的文 ...

随机推荐

  1. Feign 400错误引发的一系列问题

    Feign 400错误引发的一系列问题 问题介绍 在使用Feign进行远程调用的时候出现非常奇怪的400错误,错误信息大概如下: feign.FeignException: status 400 re ...

  2. 纯代码实现wordpress图片本地化【5.2.2版本可用】

    最近用wordpress做了个网站,想复制些新闻过来用,但图片是别人的,就想怎么本地化,在网找到了方法,那就保存一下方法. 复制下面的代码,然后粘贴到你当前WordPress主题的模版函数(funct ...

  3. NER为什么那么难

    命名实体识别(Name Entity Recognition) 是自然语言处理中一个比较基础的问题.要解决的问题是,从unstructure的文本当中找到实体并归类.当然我这么定义已经有了一定的bia ...

  4. 10.10 Rewrite 实战

    将baidu.com跳转到www.baidu.com server { listen 80; server_name baidu.com; rewrite ^/ http://www.baidu.co ...

  5. 10 月 30 日 北京 LiveVideoStack 阿里云视频云专场限量赠票 100 张

    10 月 30 日 | 北京 LiveVideoStack 将携手阿里云共邀 4 位技术大咖,一同探讨从上云到创新,视频云的新技术与新场景.阿里云视频云依托阿里云服务数百万开发者的卓越服务能力与实践, ...

  6. 洛谷3973 TJOI2015线性代数(最小割+思维)

    感觉要做出来这个题,需要一定的线代芝士 首先,我们来观察这个柿子. 我们将\(B\)的权值看作是收益的话,\(C\)的权值就是花费. 根据矩阵乘法的原理,只有当\(a[i]和a[j]\)都为\(1\) ...

  7. 小白自制Linux开发板 四. 通过SPI使用ESP8266做无线网卡

    本文章基于 WhyCan Forum(哇酷开发者社区) https://whycan.com/t_4149.htmlhttps://whycan.com/t_5870.html整理而成. 为了尊重原作 ...

  8. 后台管理系统使用vue-element-admin搭建

    近期在搞一个会议健康申报系统时,要搞一个后台,用到了vue-element-admin模板,使用的是PanJianChen(源码地址:https://github.com/PanJiaChen/vue ...

  9. vs2010中release模式下调试程序

    debug模式调试信息全,但是速度很慢,在数据量比较大的时候非常影响调试效率,release模式速度快,但是没有调试信息.所以在编译的时候很多编译器会提供一种折中的编译方式,在release下提供调试 ...

  10. Linux C 数据结构 ->单向链表

    之前看到一篇单向链表的博文,代码也看着很舒服,于是乎记录下来,留给自己~,循序渐进,慢慢 延伸到真正的内核链表~(敢问路在何方?路在脚下~) 1. 简介 链表是Linux 内核中最简单,最普通的数据结 ...