一、进程和线程        

        在java语言中最大的特点就是支持多线程的开发(也是为数不多支持多线程开发的语言),如果对多线程没有一个全面而且细致的了解,在以后一定存在严重的技术缺陷。

        进程:传统的dos是单进程处理,允许一个程序执行,后来到了Windows时代,出现了多线程的设计。表示在一个时间段上可以运行多个程序,并且这些程序将进行资源的轮流抢占。在同一时间点上,只有一个程序执行,后来有了多核CPU的实现。可以实现在同一时间点,多个程序同时执行。

        线程:是进程的基础之上划分的更小的程序单元,线程依赖于进程的实现,线程的启动要比进程快的多。

二、Thread实现多线程

 范例:  多线程主体类



class MyThread extends  Thread{
    private String title;

    public MyThread(String title){
        this.title = title;
    }
    @Override
    public void run() {
        for (int i = 0; i< 10; i++) {
             System.out.println(this.title+"运行,i="+i);
        }
    }
}



    多线程要执行的功能都应该在run()方法中进行定义,run()方法不能被直接调用(如果我们直接调用子线程的run()方法,其方法还是运行在主线程中,代码在程序中是顺序执行的,所以不会有解决耗时操作的问题。所以不能直接调用线程的run()方法,只有子线程开始了,才会有异步的效果。当thread.start()方法执行了以后,子线程才会执行run()方法,这样的效果和在主线程中直接调用run()方法的效果是截然不同的)。所以如果要开启多线程,必须执行start()方法。


public static void main(String[] args) {
    new MyThread("线程A").start();
    new MyThread("线程B").start();
    new MyThread("线程C").start();
}


   执行结果:线程A,B,C交替执行,执行顺序不可控。

   结论:虽然调用是start()方法,但是最终执行的是run()方法。

   疑问:为什么不直接使用run()方法,而使用start()?

   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();


    分析start()方法源代码:抛出“IllegalThreadStateException”异常,没有throws也没有try..catch,表明此异常一定是RuntimeException的子类。一个线程只允许启动一次,否则就会抛出此异常。

    列如:


public static void main(String[] args) {
    Thread t = new MyThread("线程A");
    t.start();
    t.start();
}


多进程处理文件 java java多进程开发_多进程处理文件 java

    native含义:

    在java执行过程中考虑到对于不同层次的开发者的需求,所以java支持有本地的操作系统函数调用,而这项技术成为JNI技术,但是Java开发过程中并不推荐这样使用,这项技术可以使用一些底层函数进行一些特殊的处理,而在start()方法里面的start0()表示需要将此方法依赖于不同的操作系统实现。

三、Runnable接口实现多线程

范例:Runnable实现


class MyThread implements  Runnable{
    private String title;

    public MyThread(String title){
        this.title = title;
    }
    @Override
    public void run() {
        for (int i = 0; i< 10; i++) {
            System.out.println(this.title+"运行,i="+i);
        }
    }
}


    此方式避免了单继承的局限,也能更好的进行功能扩展。以后的开发中,优先考虑Runnable来实现,但是启动多线程永远都是通过Thread对象来启动多线程。

    lambda表达式写法:


public static void main(String[] args) {

    for (int i =0; i <3 ; i++){
        String title="线程对象_"+i;
        Runnable run=()->{
            for (int j = 0; j< 10; j++){
                System.out.println(title+"运行,j="+j);
            }
        };
        new Thread(run).start();
    }

}


更简洁:


public static void main(String[] args) {

    for (int i =0; i <3 ; i++){
        String title="线程对象_"+i;
        new Thread(()->{
            for (int j = 0; j< 10; j++){
                System.out.println(title+"运行,j="+j);
            }
        }).start();
    }

}


四、Thread和Runnable的关系

    Thread也是Runnable接口的子类

    关系图:

    多线程的设计过程中,使用了代理模式,用户自定义的MyThread的类只负责项目核心功能的实现吗,而所有的辅助实现全部由Thread类来处理。

    调用start()方法时候,通过Thread的构造函数传递了一个Runnable的接口对象,此对象将会被target保存,最终调用的将是Runnable子类覆盖了Runnable接口的run()方法。

多线程开发示意图:

多进程处理文件 java java多进程开发_多进程处理文件 java_02

范例:通过卖票程序来实现多个线程的资源并发访问


class MyThread implements  Runnable{
    private int  ticket = 5;

    @Override
    public void run() {
        for (int i = 0; i< 100; i++) {
            if(this.ticket> 0) {
                System.out.println("卖票,ticket=" + ticket--);
            }
        }
    }
}

public class Main {

    public static void main(String[] args) {
        MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();

    }
}


执行结果:

多进程处理文件 java java多进程开发_多线程_03

内存分析:

五、Callable实现多线程

    如果要实现多线程,肯定依靠的是Runnable,但是Runnable中的run()没有返回值。所以JDK1.5后出现java.util.concurrent.Callable来实现多线程。

    接口的定义:


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


从定义可以发现Callable定义了一个泛型,此泛型的类型就是返回数据的类型,这样的好处是避免向下转型的安全隐患。

Callable的相关关系图:

实例:Callable的线程实现

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<String>{

    @Override
    public String call() throws Exception {
        for(int i = 0; i< 10 ; i++){
            System.out.println("线程执行,i="+i);
        }
        return "程序执行完毕";
    }
}

public class Main {

    public static void main(String[] args) {
        try {
            FutureTask<String> task = new FutureTask<>(new MyThread());
            new Thread(task).start();
            System.out.println("【线程返回数据】"+task.get());
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}


执行结果:

多进程处理文件 java java多进程开发_java_04

注:Runnable和Callable的区别?

1.Runnable是在JDK1.0的时候提出的多线程的实现接口,而Callable是在JDK1.5以后提出的。

2.java.util.Runnable接口只有一个run()方法,没有返回值,java.util.concurrent.Callable接口里面只有一个call()方法,可以有返回值。

六、线程的运行状态

对于多线程的开发而言,总是先定义线程主题类,通过Thread进行线程的启动,当你调用start()方法并不意味着线程已经启动。

线程的运行状态:

多进程处理文件 java java多进程开发_python_05

1.任何一个线程对象都应该使用Thread来进行封,并且通过start()进行启动,启动的时候,线程就会进入一种就绪状态,并没有执行。

2.进入到就绪状态后,就需要等待资源调度,当某一个线程调度成功后进入运行状态(run()),但是所有的线程不可能一直持续的执行下去,中间需要产生一些暂停的状态。例如:某个线程执行一段时间后让出资源,而后,这个线程就将进入阻塞状态。随后重新回到就绪状态。

3.当run()方法执行完毕后,线程的主要任务也就完成了,此时就可以进入停止状态。