多线程基础

Java 多线程编程

引言:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

线程生命周期

在这里插入图片描述

  1. 新建状态: 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序
    start() 这个线程。
  2. 就绪状态: 当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
  3. 运行状态: 如果就绪状态的线程获取 CPU 资源,就可以执行run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
  4. 阻塞状态: 如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
    • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
    • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
    • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  5. 死亡状态: 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

线程创建

1. 创建方式一:继承Thread类

   /**
     * @classDesc: 功能描述:(创建多线程例子-Thread类 重写run方法)
     * @author: flyfish
     * @version: v1.0
     */
    class CreateThread extends Thread {
    
        @Override
        public void run() {
            // run方法中编写 多线程需要执行的代码
            for (int i = 0; i < 10; i++) {
                System.out.println("i:" + i);
            }
        }
    }
    public class ThreadDemo {
        public static void main(String[] args) {
            System.out.println("-----多线程创建开始-----");
            // 1.创建一个线程
            CreateThread createThread = new CreateThread();
            // 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法
            System.out.println("-----多线程创建启动-----");
            createThread.start();
            System.out.println("-----多线程创建结束-----");
            System.out.println("-----主线程结束-----");
        }
    }

2. 创建方式二:实现Runnable 接口

/**
 * @classDesc: 功能描述:(创建多线程例子-Runnable接口 重写run方法)
 * @author: flyfish
 * @version: v1.0
 */
class CreateRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("i:" + i);
        }
    }
}
/**
 * @classDesc: 功能描述:(实现 Runnable接口,重写run方法)
 * @author: flyfish
 * @version: v1.0
 */
public class ThreadDemo2 {
    public static void main(String[] args) {
        System.out.println("-----多线程创建开始-----");
        // 1.创建一个线程
        CreateRunnable createThread = new CreateRunnable();
        // 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法
        System.out.println("-----多线程创建启动-----");
        Thread thread = new Thread(createThread);
        thread.start();
        System.out.println("-----多线程创建结束-----");
        System.out.println("-----主线程结束-----");
    }
}

3. 创建方式一:使用匿名内部类

/**
 1. @classDesc: 功能描述:(使用匿名内部类)
 2. @author: flyfish
 3. @version: v1.0
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        System.out.println("-----多线程创建开始-----");
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i< 10; i++) {
                    System.out.println("i:" + i);
                }
            }
        });
        thread.start();
        System.out.println("-----多线程创建结束-----");
    }
}

4. 通过 Callable 和 Future 创建线程

  1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
  2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
  3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
  4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
public class CallableThreadTest implements Callable<Integer> {

    public static void main(String[] args) {
        CallableThreadTest ctt = new CallableThreadTest();
        FutureTask<Integer> ft = new FutureTask<>(ctt);
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
            if (i == 20) {
                new Thread(ft, "有返回值的线程").start();
            }
        }
        try {
            System.out.println("子线程的返回值:" + ft.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Integer call() throws Exception {
        int i = 0;
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
        return i;
    }
}

创建线程的三种方式的对比

  1. 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。

  2. 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。

守护线程

Java中有两种线程,一种是用户线程,另一种是守护线程。
用户线程是指用户自定义创建的线程,主线程停止,用户线程不会停止,线程会一直执行
守护线程当进程不存在或主线程停止,守护线程也会被停止。
使用setDaemon(true)方法设置为守护线程

/*
 * @classDesc: 功能描述:(守护线程)
 * @author: flyfish
 * @createTime: 2019年8月20日 下午8:55:58
 * @version: v1.0
 */
public class DaemonThread {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                    System.out.println("我是子线程...");
                }
            }
        });
        //开启守护线程(线程会跟随主线程终止而终止)
        thread.setDaemon(true);
        thread.start();
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
            }
            System.out.println("我是主线程");
        }
        System.out.println("主线程执行完毕!");
    }
}

join() 的使用

join作用是让其他线程变为等待, t1.join();// 让其他线程变为等待,直到当前t1线程执行完毕,才释放。
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

class JoinThread implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "---i:" + i);
        }
    }
}

/**
 * @classDesc: 功能描述:(Join方法)
 * @author: flyfish
 * @createTime: 2017年8月20日 下午9:23:30
 */
public class JoinThreadDemo {

    public static void main(String[] args) {
        JoinThread joinThread = new JoinThread();
        Thread t1 = new Thread(joinThread);
        Thread t2 = new Thread(joinThread);
        t1.start();
        t2.start();
        try {
            //其他线程变为等待状态,等t1线程执行完成之后才能执行join方法。
            t1.join();
        } catch (Exception e) {

        }
        for (int i = 0; i < 100; i++) {
            System.out.println("main ---i:" + i);
        }
    }
}

线程优先级

现代操作系统基本采用时分的形式调度运行的线程,线程分配得到的时间片的多少决定了线程使用处理器资源的多少,也对应了线程优先级这个概念。在JAVA线程中,通过一个int
priority来控制优先级,范围为1-10,其中10最高,默认值为5。
下面是源码(基于1.8)中关于priority的一些量和方法。

class PrioritytThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().toString() + "---i:" + i);
        }
    }
}
/**
 * @classDesc: 功能描述:(Join方法)
 * @author: flyfish
 * @createTime: 2017年8月20日 下午9:23:30
 */
public class ThreadDemo4 {

    public static void main(String[] args) {
        PrioritytThread prioritytThread = new PrioritytThread();
        Thread t1 = new Thread(prioritytThread);
        Thread t2 = new Thread(prioritytThread);
        t1.start();
        // 注意设置了优先级, 不代表每次都一定会被执行。 只是CPU调度会有限分配
        t1.setPriority(10);
        t2.start();
    }
}

yield()的用法

Thread.yield()方法的作用:暂停当前正在执行的线程,并执行其他线程。(可能没有效果)
yield()让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。
结论:大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

public class TestYield {

    public static void main(String[] args) {
        MyThread t1 = new MyThread("t1");
        MyThread t2 = new MyThread("t2");
        t1.start();
        t2.start();
    }
}
class MyThread extends Thread {
    MyThread(String s) {
        super(s);
    }
    @Override
    public void run() {
        for (int i = 0; i <= 30; i++) {
            System.out.println(getName() + ":" + i);
            if (("t1").equals(getName())) {
                if (i == 0) {
                    yield();
                }
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值