Java多线程(上)

Java多线程

程序、进程和线程html

1、程序java

  • 程序是存储在磁盘上, 包含可执行机器指令和数据的静态实体。 即进程或者任务是处于活动状态的计算机程序。

2、进程安全

  • 进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例,即运行中的程序。网络

  • 一个运行着的程序,可能有多个进程。进程在操做系统中执行特定的任务。多线程

  • 程序运行时系统就会建立一个进程,并为它分配资源,而后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。ide

3、 线程函数

  • 线程就是程序的执行路线,即进程内部的控制序列,或者说是进程的子任务。
  • 线程,轻量级,不拥有本身独立的内存资源,共享进程的代码区、数据区、堆区(注意没有栈区)、环境变量和命令行参数、文件描述符、信号处理函数、当前目录、用户ID和组ID等资源。
  • 线程拥有本身独立的栈,所以也有本身独立的局部变量。
  • 一个进程能够同时拥有多个线程,即同时被系统调度的多条执行路线,但至少要有一个主线程。

线程实现

继承Thread类

线程是程序中执行的线程。Java虚拟机容许应用程序同时运行多个执行线程。测试

每一个线程都有优先权。 具备较高优先级的线程优先于具备较低优先级的线程执行。 每一个线程可能也可能不会被标记为守护进程。 当在某个线程中运行的代码建立一个新的Thread对象时,新线程的优先级最初设置为等于建立线程的优先级,而且当且仅当建立线程是守护进程时才是守护进程线程。this

当Java虚拟机启动时,一般会有一个非守护进程线程(一般调用某个指定类的名为main的方法)。 Java虚拟机继续执行线程,直到发生如下任一状况:操作系统

  • 已调用类Runtimeexit方法,而且安全管理器已容许执行退出操做。
  • 经过调用run方法返回或抛出超出run方法传播的异常,全部非守护程序线程的线程都已死亡。

建立线程方式一:继承Thread类,重写run()方法,调用start()开启线程

public class MyThread extends Thread{
    @Override
    public void run() {
        //子线程方法
        for (int i = 0; i < 20; i++) {
            System.out.println("我是子线程" + i);
        }
    }

    public static void main(String[] args) {
        //开启子线程
        new MyThread().start();
        
        for (int i = 0; i < 20; i++) {
            System.out.println("我是主线程" + i);
        }
    }
}

执行结果

能够发现,主线程和子线程是”同时“进行的。

Thread类实现了Runnable接口,内部经过静态代理调用了run()方法

实现Runnable接口

public class MyThread implements Runnable{
    @Override
    public void run() {
        //子线程方法
        for (int i = 0; i < 20; i++) {
            System.out.println("我是子线程" + i);
        }
    }

    public static void main(String[] args) {
        //建立线程对象,代理线程
        new Thread(new MyThread()).start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我是主线程" + i);
        }
    }
}

使用方法基本和继承Thread类相同,执行结果也类似。

实现Callable接口

  • 实现Callable接口,须要返回值类型
  • 重写call方法,须要抛出异常
  • 建立目标对象
  • 建立执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
  • 提交执行: Future<Boolean> result1 = ser. submit(t1);
  • 获取结果: boolean r1 = result1.get()
  • 关闭服务: ser. shutdownNow();

示例代码

public class MyThread implements Callable<Boolean> {
    @Override
    public Boolean call() {
        //子线程方法
        System.out.println("执行了子线程");
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread t1 = new MyThread();
        //建立执行服务
        ExecutorService ser = Executors.newFixedThreadPool(1);
        //提交执行
        Future<Boolean> result1 = ser.submit(t1);
        //获取结果
        boolean r1 = result1.get();
        //关闭服务
        ser. shutdownNow();
    }
}

线程状态

线程五大状态

  • 建立状态
  • 就绪状态
  • 阻塞状态
  • 运行状态
  • 死亡状态

五个状态的转化


建立状态:

Thread t = new Thread()

线程对象一旦建立就进入到了新生状态。

就绪状态:

当调用start()方法,线程当即进入就绪状态,但不意味着当即调度执行。

运行状态:

进入运行状态,线程才真正执行线程体的代码块。

阻塞状态:

当调用sleep, wait 或同步锁定时,线程进入阻塞状态,就是代码不往下执行,阻塞事件解除后,从新进入就绪状态,等待cpu调度执行。

不必定每一个线程都会进入阻塞状态

死亡状态:

线程中断或者结束,一旦进入死亡状态,就不能再次启动。

方法 说明
setPriority(int newPriority) 更改线程的优先级
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
void join() 等待该线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其余线程
void interrupt() 中断线程,别用这个方式
boolean isAlive() 测试线程是否处于活动状态

线程中止

  • 开始线程可使用start()方法。
  • 建议线程正常中止,利用次数,不建议死循环。
  • 建议使用标志位,设置一个标志位。
  • 不要使用stop或者destroy等过期或者JDK不建议使用的方法。

使用标志位的例子

public class MyThread implements Runnable{

    private boolean flag = true;

    @Override
    public void run() {
        //子线程方法
        int i = 0;
        while (flag) {
            System.out.println("我是子线程" + i++);
        }
    }

    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        new Thread(thread).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("主线程" + i);
            if (i == 800) {
                thread.stop();
                System.out.println("线程中止了");
            }
        }
    }
}

结果如图

线程休眠

  • sleep (时间)指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException
  • sleep时间达到后线程进入就绪状态
  • sleep能够模拟网络延时,倒计时
  • 每个对象都有一-个锁, sleep不会释放锁

线程休眠的例子:

public class MyThread{
    public static void main(String[] args) throws InterruptedException {
        while (true) {
            Date date = new Date(System.currentTimeMillis());
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
            Thread.sleep(1000);
        }
    }
}

每一秒输出当前时间,使用sleep(1000)来使得每次执行完休眠一秒。

线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让cpu从新调度,礼让不-定成功!看CPU心情

示例

public class MyThread{
    public static void main(String[] args) {
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName() + "线程开始");
            Thread.yield(); //线程礼让
            System.out.println(Thread.currentThread().getName() + "线程结束");
        };

        new Thread(runnable, "a").start();
        new Thread(runnable, "b").start();
        new Thread(runnable, "c").start();
    }
}

执行结果

能够发现每次的结果都不相同

线程联合

能够想象成一个VIP线程,强制插队,查到当前线程,直到本身执行完毕。很是霸道

会使得线程阻塞。

public class MyThread{
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("VIP线程来了" + i);
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();

        for (int i = 0; i < 50; i++) {
            if (i == 30) thread.join();
            System.out.println("主线程" + i);
        }
    }
}

结果如图

线程状态观测

  • 线程状态。线程能够处于如下状态之一:

    • NEW
      还没有启动的线程处于此状态。
    • RUNNABLE
      在Java虚拟机中执行的线程处于此状态。
    • BLOCKED
      被阻塞等待监视器锁定的线程处于此状态。
    • WAITING
      无限期等待另外一个线程执行特定操做的线程处于此状态。
    • TIMED_WAITING
      正在等待另外一个线程执行最多指定等待时间的操做的线程处于此状态。
    • TERMINATED
      已退出的线程处于此状态。

    线程在给定时间点只能处于一种状态。 这些状态是虚拟机状态,不反映任何操做系统线程状态。

经过如下代码演示线程状态的监听

public class MyThread{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ignored) {}
            }

        });
        System.out.println(thread.getState());
        thread.start();
        while (thread.getState() != Thread.State.TERMINATED) {
            Thread.sleep(500);
            System.out.println(thread.getState());
        }
    }
}

运行结果

线程的优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的全部线程, 线程调度
器按照优先级决定应该调度哪一个线程来执行。

观察Thread类源代码,能够看到优先级在jdk中的定义

/**
  * The minimum priority that a thread can have.
  */
 public static final int MIN_PRIORITY = 1;

/**
  * The default priority that is assigned to a thread.
  */
 public static final int NORM_PRIORITY = 5;

 /**
  * The maximum priority that a thread can have.
  */
 public static final int MAX_PRIORITY = 10;
  • 线程的优先级用数字表示,范围从1~10.
    • Thread.MIN_ PRIORITY= 1;
    • Thread.MAX_ PRIORITY= 10;
    • Thread.NORM_ PRIORITY = 5;

咱们能够经过

System.out.println(Thread.currentThread().getPriority());

来输出当前线程的优先级,能够获得默认优先级是5

可使用如下方式改变或获取优先级

getPriority() //获得优先级
setPriority(int xxx)  设置优先级

若是设置的优先级超出限制,则会抛出一个IllegalArgumentException异常:

if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
    throw new IllegalArgumentException();
}

优先级低只是意味着得到调度的几率低,并非优先级低就不会被调用了,这都是看CPU的调度。

最好在start前设置优先级

守护(daemon)线程

  • 线程分为用户线程守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如,后台记录操做日志,监控内存垃圾回收等待..

所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,好比垃圾回收线程就是一个很称职的守护者,而且这种线程并不属于程序中不可或缺的部分。所以,当全部的非守护线程结束时,程序也就终止了,同时会杀死进程中的全部守护线程。反过来讲,只要任何非守护线程还在运行,程序就不会终止。

用户线程守护线程二者几乎没有区别,惟一的不一样之处就在于虚拟机的离开:若是用户线程已经所有退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 由于没有了被守护者,守护线程也就没有工做可作了,也就没有继续运行程序的必要了。

演示,先定义两个线程,一个守护线程,一个普通线程

Thread daemon = new Thread(()->{
    while (true) {
        System.out.println("守护线程...");
    }
});
Thread normal = new Thread(()->{
    for (int i = 0; i < 10; i++) {
        System.out.println("子线程...");
    }
    System.out.println("子线程结束");
});

而后设置守护线程

daemon.setDaemon(true);

启动两个线程后,能够看到,守护线程并无结束就中止了程序。

Java多线程(下)https://www.cnblogs.com/chaofanq/p/15055853.html

相关文章
相关标签/搜索