Java多线程01——多线程的创建

1 进程和线程

进程:

  • 进程是并发执行程序在执行过程中,资源分配和管理的基本单位。

  • 进程可以理解为一个应用程序的执行过程,应用程序一旦执行,就是一个进程。

线程:

  • 线程是进程的一个执行单元,是进程内可调度实体。

  • 线程是比进程更小的独立运行的基本单位。

  • 线程也被称为轻量级进程。

二者的区别:

名称进程线程
地址空间不同的进程之间的地址空间是独立的同一进程的所有线程共享本进程的地址空间
资源拥有进程之间的资源是独立的,无法共享同一进程的所有线程共享本进程的资源
执行过程每一个进程可以说就是一个可执行的应用程序线程不能够独立执行,必须依存在应用程序中

2 创建线程的五种方式

 

2.1 继承 Thread

通过继承Thread并且重写其run()方法,run()方法中定义需要执行的任务。

创建后的子类通过调用start()方法即可执行线程方法。

注意: 通过继承Thread创建的线程类,多个线程间无法共享线程类的实例变量。 需要创建不同Thread对象,自然不共享资源。

/*
定义线程类,继承Thread
重写run()方法
创建线程类对象
调用start()方法启动线程
*/
public class UserThread extends Thread {
    @Override
    public void run() {
        for(int i=0;i<3;i++){
            System.out.println(Thread.currentThread().getName() + " is running " + i);
        }
    }
}

public class TestThread {
    public static void main(String[] args) {
        for(int i=0;i<2;i++){
            new UserThread().start();
        }
    }
}
复制代码

输出如下:

Thread-0 is running 0

Thread-1 is running 0

Thread-1 is running 1

Thread-1 is running 2

Thread-0 is running 1

Thread-0 is running 2

2.2 实现 Runnable 接口

需要先定义一个类实现 Runnable 接口并重写该接口的 run() 方法,此run()方法是线程执行体。

接着创建Runnable实现类的对象,作为创建Thread对象的参数target,此Thread对象才是真正的线程对象。

利用实现Runnable接口的线程类创建对象,可以实现线程之间的资源共享

/*
定义线程类,实现 Runnable接口
重写run()方法
创建实现类对象
创建Thread类,并将线程类对象参数传入Thread构造方法中
启动线程
*/
public class UserRunnable implements Runnable {
    @Override
    public void run() {
        for(int i=0;i<3;i++) {
            System.out.println(Thread.currentThread().getName() + " is running" + i);
        }
    }
}
public class TestUserRunnable {
    public static void main(String[] args) {
        UserRunnable userRunnable = new UserRunnable();
        new Thread(userRunnable).start();
        new Thread(userRunnable).start();
    }
}
复制代码

输出如下:

Thread-0 is running 0

Thread-1 is running 0

Thread-1 is running 1

Thread-1 is running 2

Thread-0 is running 1

Thread-0 is running 2

2.3 实现 Callable 接口实现带有返回值的线程

Callable 接口如同 Runnable 接口的升级版,其提供的 call() 方法将作为线程的执行体,同时允许有返回值

Callable 对象不能直接作为 Thread 对象的target,因为 Callable 接口是 Java5 新增接口,不是 Runnable 接口的子接口。

对于这个问题的解决方案,就引入 Future 接口,此接口可以接受 call() 的返回值,RunnableFuture 接口是 Future 接口和 Runnable 接口的子接口,可以作为 Thread 对象的target。

import java.util.concurrent.Callable;

/**
 * 定义线程类UserCallable,实现Callable接口
 * 重写call()方法
 * 创建UserCallable对象
 * 创建 FutureTask(实现了接口 RunnableFuture) 的对象,构造函数的参数是 UserCallable 的对象
 * 创建 Thread 对象,构造函数的参数是 FutureTask 对象
 * 启动线程
 */
public class UserCallable implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName() + " 启动!");
        return "我重写了call方法";
    }
}

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TestUserCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        UserCallable userCallable = new UserCallable();
        FutureTask futureTask = new FutureTask(userCallable);
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
    }
}
复制代码

输出如下:

Thread-0 启动!

我重写了call方法

2.4 继承 TimerTask

TimerTimerTask 可以作为实现线程的另一种方式。

Timer 是一种线程设施,用于安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行,可以看成一个定时器,可以调度 TimerTask

TimerTask 是一个抽象类,实现了 Runnable 接口,所以具备了多线程的能力。

多线程类

import java.util.Date;
import java.util.TimerTask;

/**
 * 创建 UserTimer 类,继承 TimerTask 抽象类
 * 创建 UserTimer 对象
 * 创建 Timer 类对象,设置任务的执行策略
 */
public class UserTimer extends TimerTask {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running " + new Date());
    }
}
复制代码

测试类

import java.util.Timer;

public class TestUserTimer {
    public static void main(String[] args) {
        UserTimer userTimer = new UserTimer();
        Timer timer = new Timer();
        timer.schedule(userTimer, 3000, 2000);
    }
}
复制代码

输出如下:

Timer-0 is running Sat Sep 18 23:10:47 CST 2021

Timer-0 is running Sat Sep 18 23:10:49 CST 2021

Timer-0 is running Sat Sep 18 23:10:51 CST 2021

Timer-0 is running Sat Sep 18 23:10:53 CST 2021

Timer-0 is running Sat Sep 18 23:10:55 CST 2021

Timer-0 is running Sat Sep 18 23:10:57 CST 2021

2.5 通过线程池启动多线程

通过 Executors 的工具类可以创建线程池。

提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行。

降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗。

方便线程并发数的管控,因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM(内存溢出),并且会造成CPU过度切换。

2.5.1 线程池一:固定大小的线程池FixThreadPool(int n)

创建有固定线程数的线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixThreadPoolTest {
    public static void main(String[] args) {
        //创建固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        //使用线程池执行任务
        for(int i=0;i<5;i++){
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    for(int j=0;j<3;j++){
                        System.out.println(Thread.currentThread().getName() + " : " + j);
                    }
                }
            });
        }

        executorService.shutdown();
    }
}
复制代码

输出如下:

pool-1-thread-1 : 0

pool-1-thread-2 : 0

pool-1-thread-2 : 1

pool-1-thread-2 : 2

pool-1-thread-1 : 1

pool-1-thread-3 : 0

pool-1-thread-3 : 1

pool-1-thread-3 : 2

pool-1-thread-2 : 0

pool-1-thread-2 : 1

pool-1-thread-1 : 2

pool-1-thread-2 : 2

pool-1-thread-3 : 0

pool-1-thread-3 : 1

pool-1-thread-3 : 2

2.5.2 线程池二:单线程池 SingleThreadPoolExecutor

单线程串行执行任务,确保任务按提交顺序执行;

当线程异常结束后,会有新的线程代替之前的线程。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutor {
    public static void main(String[] args) {
        ExecutorService es = Executors.newSingleThreadExecutor();
        for(int i=0;i<3;i++){
            es.submit(new Runnable() {
                @Override
                public void run() {
                    for(int j=0;j<3;j++){
                        System.out.println(Thread.currentThread().getName() + " : " + j);
                    }
                }
            });
        }
        es.shutdown();
    }
}
复制代码

输出如下:

pool-1-thread-1 : 0

pool-1-thread-1 : 1

pool-1-thread-1 : 2

pool-1-thread-1 : 0

pool-1-thread-1 : 1

pool-1-thread-1 : 2

pool-1-thread-1 : 0

pool-1-thread-1 : 1

pool-1-thread-1 : 2

2.5.3 线程池三:缓存线程池 CachedThreadPool()

线程池数量不固定,可以达到最大值。

线程可被重复利用和回收。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadExecutor {
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        for(int i=0;i<5;i++){
            es.submit(new Runnable() {
                @Override
                public void run() {
                    for(int j=0;j<3;j++){
                        System.out.println(Thread.currentThread().getName() + " : " + j);
                    }
                }
            });
        }
        es.shutdown();
    }
}
复制代码

输出如下:

pool-1-thread-2 : 0

pool-1-thread-2 : 1

pool-1-thread-2 : 2

pool-1-thread-5 : 0

pool-1-thread-5 : 1

pool-1-thread-5 : 2

pool-1-thread-4 : 0

pool-1-thread-4 : 1

pool-1-thread-4 : 2

pool-1-thread-3 : 0

pool-1-thread-3 : 1

pool-1-thread-1 : 0

pool-1-thread-1 : 1

pool-1-thread-1 : 2

pool-1-thread-3 : 2

2.5.4 线程池四:周期性的线程池 newScheduledThreadPool()

创建一个周期性的线程池,支持定时及周期性执行任务

创建线程时,指定核心线程数,当执行任务较多超过核心线程时,可额外启动新的线程;

当任务恢复后,仅保留核心线程,其它额外线程将被关闭。

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadTest {
    public static void main(String[] args) {
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(5);
        ses.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " 执行 " + new Date());
            }
        }, 5, 3, TimeUnit.SECONDS);
    }
}
复制代码

输出如下:

pool-1-thread-1 执行 Sun Sep 19 00:58:55 CST 2021

pool-1-thread-1 执行 Sun Sep 19 00:58:58 CST 2021

pool-1-thread-2 执行 Sun Sep 19 00:59:01 CST 2021

pool-1-thread-1 执行 Sun Sep 19 00:59:04 CST 2021

pool-1-thread-3 执行 Sun Sep 19 00:59:07 CST 2021

pool-1-thread-3 执行 Sun Sep 19 00:59:10 CST 2021

2.5.5 线程池五:新的线程池类 ForkJoinPool 的扩展 newWorkStealingPool

JDK1.8增加, 任务窃取线程池,线程有属于自己的队列,更加适用于多核心处理器。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class WorkStealingPoolTest {
    public static void main(String[] args) {
        ExecutorService es = Executors.newWorkStealingPool();
        for(int i=0;i<10;i++){
            es.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---END----");
    }
}
复制代码

输出如下:

ForkJoinPool-1-worker-1

ForkJoinPool-1-worker-1

ForkJoinPool-1-worker-1

ForkJoinPool-1-worker-2

ForkJoinPool-1-worker-3

ForkJoinPool-1-worker-3

ForkJoinPool-1-worker-3

ForkJoinPool-1-worker-3

ForkJoinPool-1-worker-2

ForkJoinPool-1-worker-1

---END----

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我有健康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值