java多线程学习总结

线程概念和两种实现方法

一、线程的概念

现代的操作系统都是多用户多进程分时操作系统,所以我们在使用操作系统时,可以一边听歌,一边下载,还可以聊天等等,事实上我们的操作系统同时还运行着很多后台进程,你可以打开window系统的任务管理器就可以看到很多进程在运行。
这里写图片描述
在往下学习之前,让我们先弄清楚什么是程序,进程和线程这几个概念。

1、程序:
利用编程语言开发的一个工具软件, 静态的,在没有启动运行之前只是磁盘中的一个普通文件。

2、进程:
程序启动之后就变成了进程,进程是在内存中运行的,具有一定的生命周期,如果程序运行解析,进程在内存中就会回收,生命周期也就结束了,当然程序是可以再次运行,重写变成进程。

3、线程:
进程在运行过程中的执行走向,线索。线程是比进程更小的一个单位,一个进程可以有一个或多个线程的。
java程序启动运行时,就自动产生了一个线程,主函数main就是在这个线程上运行的,当不再产生新的线程时,程序就是单线程。到目前为止我们编写的所有的java程序都是单线程的,接下来我们要学习如何实现多线程的java程序。

二、线程的实现方法

1、单线程。
我们新建一个类Stu1实现一个简单的循环。

public class Stu1 {

    public static void main(String[] args) {
        //主线程
        for(int i=0;i<10;i++){
            System.out.println("主线程:"+i);
        }
        System.out.println("主线程:死亡");
    }

}

运行上面代码,程序只有一个线程,运行完毕程序也就结束了。

2、通过继承Thread类实现第一个线程。

新建一个Thread1类继承Thread类,并重写run方法,run方法就是线程的执行内容。实现代码如下:

public class Thread1 extends Thread{
    //线程体
    public void run(){
        for(int i=0;i<5;i++){
            System.out.println("线程1:"+i);
        }
        System.out.println("线程1:死亡");
    }
}

我们要启动这个线程不是调用run()方法,而是使用start()方法启动线程,线程启动后会自动执行run()方法。
修改上面的Stu1的main方法,启动这个线程。修改如下:

public static void main(String[] args) {
        Thread1 thread1=new Thread1();
        thread1.start();//通过start启动线程,他会自动执行run方法

        //主线程
        for(int i=0;i<5;i++){
            System.out.println("主线程:"+i);
        }
        System.out.println("主线程:死亡");    
    }

现在你可以再次运行这个段代码,查看后台的输出。你可以多运行几次,我的其中一次的输出如下:

主线程:0
线程1:0
主线程:1
线程1:1
主线程:2
线程1:2
主线程:3
线程1:3
主线程:4
线程1:4
主线程:死亡
线程1:死亡

由输出可见,主线程和子线程之间是独立运行的,它们将会轮流的占用CPU,而那个线程会占有CPU是由操作系统决定的。所以我们看到多次运行这个程序时,每一次的输出可能都不一样。

3、通过实现Runnable接口实现线程。

为了克服java单继承的限制,java提供了另外一种实现线程类的方式,就是实现Runnable接口,因为接口是可以同时实现多个接口的。同样需要实现run方法。实现代码如下:

public class Thread2  implements Runnable{

    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println("线程2:"+i);
        }
        System.out.println("线程2:死亡");
    }

}

启动这个线程也有点区别,你需要new一个Thread类,并把这个实现类作为参数传入这个Thread类,修改Stu1的main方法如下:

public static void main(String[] args) {

        Thread1 thread1=new Thread1();
        thread1.start();//通过start启动线程,他会自动执行run方法

        //启动另外一个线程,该线程实现了Ruannable接口
        Thread t2=new Thread(new Thread2());
        t2.start();

        //主线程
        for(int i=0;i<10;i++){
            System.out.println("主线程:"+i);
        }
        System.out.println("主线程:死亡");

    }

再次运行该程序,现在程序共有三个线程,每个线程还是独立的,所以输出的结果感觉也是错乱的。

4、其他的方法。
事实上面我们可以通过setPriority设置优先级别,当然设置优先级别也只是一个给操作系统一个建议,最后谁先占用CPU还是按照操作系统自己的算法。

另外我们也可以通过sleep()方法让线程休眠,这样他就不会占用CPU了。参考代码如下:

public static void main(String[] args) {

        Thread1 thread1 = new Thread1();
        thread1.setPriority(10);
        thread1.start();// 通过start启动线程,他会自动执行run方法

        Thread t2 = new Thread(new Thread2());
        t2.start();

        //主线程休眠
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // 主线程
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程:" + i);
        }
        System.out.println("主线程:死亡");

    }

运行上面的代码,我们发现主线程的循环要等待两秒后才输出,而线程1的优先级别是最高的。

三、java线程的状态

每个线程都有一个从初始状态到死亡状态的生命周期,其间根据业务流程经过就绪状态(runnable)、运行状态(runing)、阻塞状态(blacked),这些状态可以根据流行需要相互转换。线程状态转换示意图
这里写图片描述

0、初始状态

跟其他Java对象一样用new语句创建的线程对象处于初始状态,此时仅被分配了内存。

1、就绪状态(Runnable)

当每个线程对象创建好后,通过调用其start()方法使得该线程就进入就绪状态,就绪状态的线程进入到JVM的可运行池中等待CPU的使用权,每个线程都可以通过设置其优先级以提高取得CPU的使用权的可能性。当然,CPU的使用权跟计算机的硬件分不开的,这体现在单CPU环境下跟多CPU(及单CPU多核)下处理是不一样的,因为同一时刻可以让几个线程占用不同的CPU。

2、运行状态

在JVM中就绪状态的线程取得CPU的使用权后,得以执行run()方法中定义的程序代码。只有处于就绪状态的线程才有机会转到运行状态。

3、阻塞状态(Blocked

运行中的线程可以根据需要放弃占用的CPU而暂停运行,这是线程就处于阻塞状态,此时Jvm不再尝试给线程分配CPU使用机会。处于阻塞状态有三种情况:

1. 位于Jvm线程等待池中的阻塞状态(Blocked in object’s wait pool):当线程处于运行状态,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中,

2.位于对象锁池中的阻塞状态(Blocked in object’s lock pool):当线程处于运行状态,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中,

3.其他阻塞状态(Otherwise Blocked):当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求,就会进入这个状态。

4、死亡状态(Deaded)

当线程正常执行完run()方法中的代码,或者遇到异常未捕获造成中断,线程就会退出运行状态,转入死亡状态,结束其生命周期。

猜你喜欢

转载自blog.csdn.net/qq_41112517/article/details/80206769