栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

JAVA多线程基础

Java 更新时间:发布时间: 百科书网 趣学号
多线程 进程与线程

进程

  • 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。

线程

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行。一个进程最少有一个线程。
  • 线程实际上是在进程基础上的进一步划分,一个进程启动后,里面的若干执行路径又可以划分成若干个线程。
  • 线程可分为守护线程和用户线程
线程调度
  • 优先让优先级别高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),JAVA使用的为抢占式调度。
  • CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻只能执行一个线程,而CPU在多个线程间切换的速度将会变快,看上去就像是在同一时刻执行。其实多线程程序并不能提高程序的运行速度,但是能够提高程序的运行效率,让CPU的使用率提高。
同步与异步

同步:排队执行,效率低但是安全。
异步:同时执行,效率高但是数据不安全。

并发与并行

并发:两个或多个事件同时在同一个时间段内发生
并行:两个或多个事件在同一时刻发生

Thread

继承了Thread的子类为线程类

run()

线程要执行的任务方法,在继承时被重写

run方法的方法体就是一条新的执行路径,其触发方式不是调用run(),而是调用Tread类的方法start()来启动任务

public static void main(String[] args) throws IOException {
        MyThread mt = new MyThread();
        mt.**start()**;
        for (int i = 0; i < 5; i++) {
            System.out.println("main"+i);
        }
    }
    
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("run"+i);
        }
    }
}
>>>
main0
main1
main2
main3
run0
main4
run1
run2
run3
run4

通过匿名内部类实现线程
    public static void main(String[] args) throws IOException {
        new Thread(){    
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("thread"+i);
                }
            }
        }.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("main"+i);
        }

    }
>>>
thread0
main2
thread1
main3
thread2
main4
thread3
thread4

构造方法
Thread()

Thread(Runnable target)

Thread(Runnable target, String name)
Thread(String name)

成员方法
public final String getName()

返回此线程的名称。 

public void run()

如果此线程是使用单独的Runnable运行对象构造的,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。 

public final void setPriority(int newPriority)

更改此线程的优先级。
public void start()

导致此线程开始执行; Java虚拟机调用此线程的run方法。 
public void stop()

**已经过时的方法,**本质是不安全的。
public static void sleep​(long millis) throws InterruptedException

导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序
的精度和准确性。 
该线程不会失去任何监视器的所有权。 

public static Thread currentThread()

返回对当前正在执行的线程对象的引用
sleep()
public static void sleep​(long millis) throws InterruptedException

导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,
具体取决于系统计时器和调度程序的精度和准确性。 该线程不会失去任何监视器的所有权。 

public static void sleep​(long millis, int nanos) throws InterruptedException

导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,
具体取决于系统定时器和调度程序的精度和准确性。 该线程不会失去任何监视器的所有权。 

参数 
millis - 以毫秒为单位的睡眠时间长度 
nanos - 0-999999睡觉的额外纳秒 

异常 
IllegalArgumentException - 如果 millis值为负数,或者值 nanos不在 0-999999范围内 
InterruptedException - 如果有任何线程中断了当前线程。 抛出此异常时,将清除当前线程的中断状态
for (int i = 0; i < 10; i++) {
            System.out.println(i);
            Thread.sleep(1000);
        }
字段
变量和类型字段描述
static intMAX_PRIORITY线程可以拥有的最大优先级
static intMIN_PRIORITY线程可以拥有的最小优先级
static intNORM_PRIORITY分配给线程的默认优先级
Runnable

实现Runnable接口的子类为线程类,其实例为一个任务对象

run()

线程要执行的任务方法,在继承时被重写,其触发方式不是调用run(),而是调用Tread类的方法start()来启动任务

实现Runnable类对象,需要:

  1. 创建一个任务对象(Runnable 类)

  2. 创建一个线程,并为其分配一个任务

  3. 执行该线程

    public static void main(String[] args) throws IOException {
        MyRunnable mr = new MyRunnable();
        Thread t = new Thread(mr);
        t.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("main"+i);
        }
    }
    public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Runnable run"+i);
        }
    }
}
>>>
main0
main1
Runnable run0
main2
Runnable run1
main3
main4
Runnable run2
Runnable run3
Runnable run4

实现Runnable与继承Thread相比的好处:
  1. 通过创建任务然后分配给线程的方式来实现的多线程,更适合多个线程同时执行相同任务的情况

  2. 可以避免单继承带来的局限性

  3. 任务与线程本身是分离的,提高了程序的健壮性

  4. 后续学习的线程池技术能接受Runnable类型的任务而不接受Thread类型的线程

设置和获取线程名称 方法
public static Thread currentThread()

返回对当前正在执行的线程对象的引用
public final String getName()

返回此线程的名称
public final void setName​(String name)

将此线程的名称更改为等于参数name
设置/获取线程名称
public class Test {
    public static void main(String[] args) throws IOException {
        System.out.println(Thread.currentThread().getName());    //主线程
        new Thread(new MyRunnable(),"他日我若为青帝").start();    //子线程
    }

    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
}
>>>
main
他日我若为青帝
public class Test {
    public static void main(String[] args) throws IOException {
        System.out.println(Thread.currentThread().getName());
        new Thread(new MyRunnable()).start();    //不设置线程名称其也会有默认的名称
        new Thread(new MyRunnable()).start();
        new Thread(new MyRunnable()).start();
    }

    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
}
>>>
main
Thread-0
Thread-2
Thread-1

线程阻塞

不仅指线程休眠,还指所有需要消耗较长时间的操作。

线程中断

一个线程的中断是一个独立的执行路径,它是否应该结束,应该由其自身决定

调用stop()可能导致强制线程停止执行,造成异常,本质上是不安全的,因此这个方法已经过时

中断标记
public void interrupt()

中断此线程
中断异常
public class InterruptedException extends Exception

设置try-catch捕获线程的中断异常

public class Test {
    public static void main(String[] args) throws IOException, InterruptedException {

        Thread t1 = new Thread(new MyRunnable());
        t1.start();

        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        t1.interrupt();    //给t1线程添加中断标记
    }

    static class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {    //如果发现中断标记
                    //e.printStackTrace();
                    System.out.println("发现中断异常,线程自杀");
                    return;    //结束任务
                }
            }

        }
    }
}
>>>
main:1
Thread-0:1
main:2
Thread-0:2
main:3
Thread-0:3
main:4
Thread-0:4
main:5
Thread-0:5
发现中断异常,线程自杀

守护线程

当最后一个用户进程结束时,所有守护线程自动死亡

public final void setDaemon​(boolean on)

将此主题标记为daemon线程或用户线程。 
当运行的唯一线程都是守护程序线程时,JVM将退出。 
**必须在启动线程之前调用此方法。 **

参数 
on - 如果为 true ,则将此线程标记为守护程序线程 
public class Test {
    public static void main(String[] args) throws IOException, InterruptedException {

        Thread t1 = new Thread(new MyRunnable());
        /
service.schedule(new Runnable() {
    @Override
    public void run() {
        System.out.println("执行任务");
    }
},5,TimeUnit.SECONDS);
周期执行
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);

service.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        System.out.println("执行任务");
    }
},5,3,TimeUnit.SECONDS);
>>>
执行任务
执行任务
执行任务
…
执行任务

lambda表达式

Lambda 表达式,也可称为闭包,允许把函数作为一个方法的参数(函数作为参数传递进方法中),是推动 Java 8 发布的最重要新特性。

体现了函数式编程思想,使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法
(parameters) -> expression
//或
(parameters) ->{ statements; }
特征
  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。

  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。

  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

实例
Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("不使用lambda表达式");
            }
        });
t.start();
Thread t = new Thread(()-> System.out.println("使用lambda表达式"));
t.start();
public static void main(String[] args) {
    print(new Mymath() {
        @Override
        public int sum(int x, int y) {
            return x+y;
        }
    },100,200);
}

public static void print(Mymath m, int x, int y) {
    int num = x + y;
    System.out.println(num);
}

static interface Mymath {
    int sum(int x, int y);
}
public static void main(String[] args) {
    print((int x, int y)->{
            return x+y;
    },100,200);
}

public static void print(Mymath m, int x, int y) {
    int num = x + y;
    System.out.println(num);
}

static interface Mymath {
    int sum(int x, int y);
}

保留参数部分,保留方法体

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/602164.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号