Java多线程整理1-概念-优势-创建-Thread 类及常见方法

多线程整理1

1. 概念

进程是系统分配资源的最小单位,线程是系统调度的最小单位。一个进程内的线程之间是可以共享资源的。
每个进程至少有一个线程存在,即主线程。
多进程与多线程本质区别:每个进程拥有自己的一整套变量,而线程则共享数据。在有些操作系统中,与进程相比较,线程的创建与撤销比启动新进程开销要小的多。

2. 优势

2.1 阻塞处理

一个业务正在处理时不影响其他人正常使用

import java.util.Scanner;

public class Test {
	//递归计算第N个斐波那契数
    private static long fib (int n) {
        if(n < 2) {
            return  n;
        }
        return fib(n - 1) + fib(n - 2);
    }
	//创建线程类
    private static class FibThread extends Thread {
        private int n;

        FibThread(int n) {
            this.n = n;
        }

        @Override
        public void run() {
            System.out.println(n+ "-fib->" + fib(n));
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (true) {
            int n = sc.nextInt();
			
			//每接收一个数就创建一个线程去计算
            FibThread thread = new FibThread(n);
            thread.start();
        }
    }
}

例如斐波那契数列第45个在计算时并不影响其继续使用
结果展示

2.2 提升速度

多线程在一些场合下是可以提高程序的整体运行效率的。并非所有多线程都可以提升运行速度。
观察:

public class Test{
    private static final long COUNT = 10_0000_0000L;

    public static void main(String[] args) throws InterruptedException {
        // 使用多线程方式
        concurrency();
        // 使用单线程方式
        serial();
    }

    private static void concurrency() throws InterruptedException {
        long begin = System.nanoTime();

        // 利用一个线程计算 a 的值
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int a = 0;
                for (long i = 0; i < COUNT; i++) {
                    a--;
                }
            }
        });
        thread.start();

        // 主线程内计算 b 的值
        int b = 0;
        for (long i = 0; i < COUNT; i++) {
            b--;
        }
        // 等待 thread 线程运行结束
        thread.join();

        // 统计耗时
        long end = System.nanoTime();
        double ms = (end - begin) * 1.0 / 1000 / 1000;
        System.out.printf("多线程: %f 毫秒%n", ms);

    }
    private static void serial() {
        // 全部在主线程内计算 a、b 的值
        long begin = System.nanoTime();
        int a = 0;
        for (long i = 0; i < COUNT; i++) {
            a--;
        }
        int b = 0;
        for (long i = 0; i < COUNT; i++) {
            b--;
        }
        long end = System.nanoTime();
        double ms = (end - begin) * 1.0 / 1000 / 1000;
        System.out.printf("单线程: %f 毫秒%n", ms);
    }
}

结果
在这里插入图片描述

3. 创建线程

3.1 方法一—继承Thread类

不推荐,如果有很多任务,要为每个任务创建一个独立的线程所付出的代价太大

class MyThread extends Thread {
  @Override
  public void run() {
    //code
 }
}
MyThread t = new MyThread();
t.start(); // 线程开始运行
3.2 方法二—实现 Runnable 接口

通过实现 Runnable 接口,并且调用 Thread 的构造方法时将 Runnable 对象作为 target 参数传入来创建线程对象。
该方法的好处是可以规避类的单继承的限制;但需要通过 Thread.currentThread() 来获取当前线程的引用。

class MyRunnable implements Runnable {
  @Override
  public void run() {
    //code
  }
}
Thread t = new Thread(new MyRunnable());
t.start(); // 线程开始运行
3.3 方法三—使用匿名类_lambda表达式创建线程对象
//使用匿名类创建 Thread 子类对象
Thread t1 = new Thread() {
	@Override
	public void run() {
		//code
	}
};
// 使用匿名类创建 Runnable 子类对象
Thread t2 = new Thread(new Runnable() {
	@Override
	public void run() {
		//code
	}
});
// 使用 lambda 表达式创建 Runnable 子类对象
Thread t3 = new Thread(() -> {
	//run() 方法
});

4. Thread 类及常见方法

4.1 Thread 的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理,分好的组即为线程组

4.2 Thread 的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  • ID 是线程的唯一标识,不同线程不会重复
  • 名称是各种调试工具用到
  • 状态表示线程当前所处的一个情况,下面会进一步说明
  • 优先级高的线程理论上来说更容易被调度到
  • 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行
  • 是否存活,即简单的理解,为 run 方法是否运行结束了
  • 线程的中断问题,下面进一步说明

4.3 启动一个线程

调用 start 方法

4.4 中断一个线程

4.4.1 使用自定义的标志位通知停止
class Condition {
    public volatile boolean running = true;
}

class B extends Thread {

    private Condition condition;

    B(Condition condition) {
        this.condition = condition;
    }

    @Override
    public void run() {
        while (condition.running) {
            System.out.println("znpy");
        }
    }

}

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        Condition condition = new Condition();

        B b = new B(condition);
        //定义变量并传入引用,使得 B线程看的条件和 main 修改的条件是同一个对象
        //B 线程才能看到修改

        b.start();

        sc.nextLine();
        condition.running = false;

    }
}
4.4.2 使用Java提供的方式通知停止
class B extends Thread {
    @Override
    public void run() {
        while (!Thread.interrupted()) {
            System.out.println("znpy");
            try {
                TimeUnit.SECONDS.sleep(5);

            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
    }
}
public class Main {
    public static void main(String[] args) {
        B b = new B();
        b.start();

        Scanner sc = new Scanner(System.in);
        sc.nextLine();
        b.interrupt();
    }
}

通过 thread 对象调用 interrupt() 方法通知该线程停止运行时
thread 收到通知的方式有两种:

  1. 如果线程调用了 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志
  2. 如不是第一种,则thread 可以通过
    1. Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
    2. Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志

4.5 等待一个线程

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)同理,但可以更高精度

4.6 获取当前线程引用

public static Thread currentThread(); 返回当前线程对象的引用

4.7 休眠当前线程

因为线程的调度是不可控的,所以,这个方法只能保证休眠时间是大于
等于休眠时间的。

方法说明
public static void sleep(long millis) throws InterruptedException休眠当前线程 millis 毫秒
public static void sleep(long millis, int nanos) throws InterruptedException可以更高精度的休眠
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值