Java线程

得意时要看淡,失意时要看开。不论得意失意,切莫大意;不论成功失败,切莫止步。志得意满时,需要的是淡然,给自己留一条退路;失意落魄时,需要的是泰然,给自己觅一条出路Java线程,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

进程与线程

进程
在操作系统中,每个独立的执行的程序都是一个进程,计算机上都是安装的多任务操作系统,能同时执行多个应用程序。

在多任务的操作系统中,逻辑上是同时运行的,实际上并不是,所有应用程序都是由CPU执行,对于CPU来说,一个时间点只能运行一个程序。操作系统会为每个进程分配一段有限的CPU占有时间,占有时间都在同一条时间线上。

线程
每个运行的程序都是一个进程,在一个程序中还可以有多个可执行的单元,每个单元称为线程。每个进程中至少存在一个线程。

当启动Java程序时,就会产生一个进程,并创建一个线程,执行主函数的代码。代码会按调用顺序从上往下依次执行,称为单线程程序,如果希望程序多端代码交替运行,则需要创建多个线程。

线程和进程一样都是由CPU执行的,在同一条时间线上,逻辑上是同时执行的。

线程创建

Java中提供了两种多线程的实现方式,一种是继承java.lang包下的Thread类,重写run()方法,在run方法类实现线程代码;另一种方法是实现java.lang.Runable接口,重写run()方法。

继承Thread

//继承Thread
import java.lang.Thread;
public class App {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.run();
        for (int i=0;i<10;i++){
            System.out.println("main is running");
        }
    }

    static class MyThread extends Thread{

        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println("MyThread is running");
            }

        }
    }
}

在这里插入图片描述

上面代码演示了一个单线的案例,run是继承Thread创建的一个线程,该线程结束后主程序才结束。

多线程需要持续运行的程序才能体现出来,系统分配CPU占用时间。

//多线程演示用start()方法,run会抢占线程
import java.lang.Thread;
public class App {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.start();
        while (true){
            System.out.println("main is running");
        }
    }

    static class MyThread extends Thread{

        @Override
        public void run() {
            while (true){
                System.out.println("MyThread is running");
            }

        }

    }
}

在这里插入图片描述

如图看出两个线程是交替运行,由系统膏分配CPU占用时间。注意:用start()开启线程,不能用run()会抢占线程。

多线程与单线程的区别在于动态占用CPU时间。单线程独占,多线程系统分配。

实现Runnable接口

Thread创建线程的弊端在于必须继承Thread,而已经存在继承关系的类就不能在继承了(Java单继承原理)。Runnable接口的优势在于其只实现了Thread的run方法,只需重写逻辑,并将Runnable作为参数传递给Thread,这样就不用继承Thread。

//将Runnable作为参数传到Thread
public class App {
    public static void main(String[] args) {
        Thread thread=new Thread(new MyRunnable());
        thread.start();

        Thread thread1=new Thread(new MyRunnableOne());
        thread1.start();
    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            while (true){
                System.out.println("Runnable is running");
            }

        }
    }

    static class MyRunnableOne implements Runnable{

        @Override
        public void run() {
            while (true){
                System.out.println("RunnableOne is running");
            }

        }
    }
}

在这里插入图片描述

线程应用

Thread和Runnable都能实现线程,那么有什么区别呢?

Thread是独立的

class MyThreadOne extends Thread{
    private int total=100;
    @Override
    public void run() {
        while (total>=1){
            //获取当前线程
            Thread thread=Thread.currentThread();
            //获取线程名
            String th_name=thread.getName();
            System.out.println(th_name+"正在发售第"+total--+"张票");
        }
    }
}

public class MainApp {

    public static void main(String[] args) {
        MyThreadOne myThreadOne1=new MyThreadOne();
        MyThreadOne myThreadOne2=new MyThreadOne();
        MyThreadOne myThreadOne3=new MyThreadOne();
        myThreadOne1.start();
        myThreadOne2.start();
        myThreadOne3.start();
    }

}

代码实现了继承Thread,内部有100张票,三个线程发售。
在这里插入图片描述
由结果可以发现线程Thread-0和1,2都各自发售了第100次票,显然他们是独立的。

Runnable线程是合作的

package com.company.runnable;

class MyRunnable implements Runnable{
    private int total=100;
    @Override
    public void run() {
        while (total>=1){
            //获取当前线程
            Thread thread=Thread.currentThread();
            //获取线程名
            String th_name=thread.getName();
            System.out.println(th_name+"正在发售第"+total--+"张票");
        }
    }
}

public class MainApp {
    public static void main(String[] args) {
        Thread myThreadOne1=new Thread(new MyRunnable());
        Thread myThreadOne2=new Thread(new MyRunnable());
        Thread myThreadOne3=new Thread(new MyRunnable());
        myThreadOne1.start();
        myThreadOne2.start();
        myThreadOne3.start();

    }
}

代码继承Runnable重写run方法,内部100张票
在这里插入图片描述
由结果可以看出所有线程共享成员变量。

除了上面案例外由多态特性也可以理解,通过实例化Thread创建是不同的对象,对象间是独立的,毫无关联的。而实现Runnable接口,仅仅是重写了run()方法,他们共享成员变量。

线程生命周期及状态转换

Java对象中任何对象都有生命周期,线程也不例外,Thread对象创建完成,线程的生命周期就开始了,当run抛出异常或阶段性关闭,线程就结束了。

线程的生命周期分为五个阶段:新建状态(New)、就绪状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、死亡状态(Terminated),线程的不同状态表明了线程当前正在运行的活动。

  1. 新建状态
    JVM分配内存,没有任何线程特征。
  2. 就绪状态
    当线程调用start()方法后,线程进入就绪状态,线程位于线程队列中,等待CPU分配。
  3. 运行状态
    获得CPU使用权后执行线程执行体,该程序处于运行状态。系统剥夺CPU使用权后,处于等待状态,等待再次占有CPU,直至程序结束。
  4. 阻塞状态
    运行的线程由于某种原因,被迫让出CPU使用权而进入阻塞状态。此时线程并没有进入等待对列,CPU由其他等待对列中的线程使用,直至解除阻塞状态,进入等待对列,等待系统分配CPU使用权后才能再次执行。
    notify()和·wait()方法可以使线程进入阻塞状态。sleep()方法使阻塞特定时间,join()加入新进程会阻塞原进程,直至新进程运行完毕。

线程调度

程序的多个线程逻辑上使并发的,某个线程要想被执行必须获取CPU占有权,由Java虚拟机按特定的机制为线程分配CPU的使用权叫线程的调度。

线程调度由两种模型:分时调度和抢占式调度。分时调度使在实现线上,按相同维度的时间片轮流分配CPU使用权。抢占式调度是按优先级分配CPU使用权。

线程优先级
Java JVM默认是抢占式调度,优先级决定了CPU分配顺序。
在这里插入图片描述
图中大写的常量设置线程优先级。

线程休眠
sleep(long millis)方法让线程休眠一段时间把CPU让给其他线程。该方法时静态方法,只能控制当前的线程休眠,休眠结束后,该线程进入等待对列。

线程让步
线程让步时正在执行的线程将CPU让给其他线程,yield()方法。

线程插队
线程可以通过join()方法在某个线程之前执行。

线程安全
多线程共享资源时由于延迟或其他原因导致的错误。

class MyRunnable implements Runnable{
    private int total=5;
    @Override
    public void run() {
        while (total>=1){
            //获取当前线程
            Thread thread=Thread.currentThread();
            //获取线程名
            String th_name=thread.getName();
            //添加延迟
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(th_name+"正在发售第"+total--+"张票");
        }
    }
}


public class MainApp {
    public static void main(String[] args) {
        Thread myThreadOne1=new Thread(new MyRunnable());
        Thread myThreadOne2=new Thread(new MyRunnable());
        Thread myThreadOne3=new Thread(new MyRunnable());
        myThreadOne1.start();
        myThreadOne2.start();
        myThreadOne3.start();

    }
}


代码模拟了10毫秒的延迟,线程执行的进度是不一样的。
在这里插入图片描述
由于线程都有10毫秒的休眠,出现了很多重复的情况。

Java提供了同步机制,当多个线程使用同一个共享资源时将执行共享资源的代码放在synchronized关键字修饰的代码块中,这个代码块被称作同步代码块。

synchronized 返回值类型 方法名([参数列表]){代码块}

synchronized (obj){
    while (total>0){
        //获取当前线程
        Thread thread=Thread.currentThread();
        //获取线程名
        String th_name=thread.getName();
        //添加延迟
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(th_name+"正在发售第"+total--+"张票");
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/156296.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!