【JavaSE】线程的使用

目录

一 、进程与线程的区别

查看jvm进程由哪些线程组成

二、线程的创建方式

方式一:继承 java.lang.Thread 类(线程子类)

方式二:实现 java.lang.Runnable 接口(线程执行类)

方式三:实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常

方式四:线程池

三 、线程的状态

四、窗口抢票案例

一 、进程与线程的区别

进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

当我们打开任务管理器时 里面的所有任务都是进程 而一个进程中至少包含一个线程

查看jvm进程由哪些线程组成

        // 获取 Java 线程管理对象 ThreadMXBean
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();

        // 不需要获取的锁监视器 lockedMonitor 和 synchronizer 信息
        // 仅获取线程和线程信息
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);

        // 遍历线程信息,仅打印线程 ID 和线程名称信息
        for (ThreadInfo threadInfo : threadInfos) {
            System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());
        }

这是我用idea查看结果

值得一提的时线程  [ 6 ]  Monitor Ctrl - Break 是idea独有的线程(Run模式)

  • [ 1 ]  main 是主线程
  • [ 2 ] Reference Handler 是清除 reference 线程
  • [ 3 ] Finalizer 是调用对象 finalize 方法的线程
  • [ 4 ] Signal Dispatcher 是分发处理给 JVM 信号的线程
  • [ 5 ] Attach Listener 是添加事件监听器

二、线程的创建方式

四种方式,但其实都是基于Thread类

方式一:继承 java.lang.Thread 类(线程子类)

public class Main {
    // 主线程main
    public static void main(String[] args) {

        //创建并启动子线程
        SubThread s1 =new SubThread("线程1");
        SubThread s2 =new SubThread("线程2");
        SubThread s3 =new SubThread("线程3");

        s1.start();
        s2.start();
        s3.start();
    }
}

// 线程子类
class SubThread extends Thread{
    public SubThread(String name) {
        super(name);
    }

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

方式二:实现 java.lang.Runnable 接口(线程执行类)

模拟同时通过JavaMail发送三分邮件(当然,并未有实际发送代码,在我写的第一篇博客中有详细代码,可以去了解。)

public class Main {
    public static void main(String[] args) {

        EmailTask emailTask =new EmailTask();

        Thread t1 =new Thread(emailTask,"线程1");
        Thread t2 =new Thread(emailTask,"线程2");
        Thread t3 =new Thread(emailTask,"线程3");

        t1.start();
        t2.start();
        t3.start();
    }
}

class EmailTask extends Task implements Runnable{


    @Override
    public void run() {
        Thread thread = Thread.currentThread();

        String name = thread.getName();

        System.out.println(name+":使用JavaMail技术,通过smtp协议发送邮件!");
    }

    @Override
    public void execute() {
        execute();

    }
}

abstract class Task{
    public abstract void execute();
}

方式三:实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常

前面两种方式都没有返回值,如果有需要返回值的业务可以使用当前方式创建线程(当前例子是想要获得0-1000的累加和,但分为三次线程同时累加后,将三次的累加和求和即可。)

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("开始分别获取。。。。。");
        //Callable
        SumCalcTask s1 =new SumCalcTask(0,500);
        SumCalcTask s2 =new SumCalcTask(501,800);
        SumCalcTask s3 =new SumCalcTask(801,1000);

        //Callable -->FutureTask(Runnable接口实现类)
        FutureTask<Integer> futureTask1 = new FutureTask<Integer>(s1);
        FutureTask<Integer> futureTask2 = new FutureTask<Integer>(s2);
        FutureTask<Integer> futureTask3 = new FutureTask<Integer>(s3);

        Thread t1 =new Thread(futureTask1);
        Thread t2 =new Thread(futureTask2);
        Thread t3 =new Thread(futureTask3);
        
        t1.start();
        t2.start();
        t3.start();

        System.out.println("汇总各自结果。。。。");
        System.out.println("最终计算结果:"+ new Integer(futureTask1.get()+futureTask2.get()+futureTask3.get()));
    }
}

class SumCalcTask implements Callable<Integer>{

    private int begin,end;
    public SumCalcTask(int begin,int end){
        this.begin=begin;
        this.end=end;
    }
    @Override
    public Integer call() throws Exception {
        int total = 0 ;
        for (int i = begin; i <=end ; i++) {
            total+=i;
        }
        System.out.println(Thread.currentThread().getName()+":计算完成!!");
        return total;
    }
}

方式四:线程池

此方式可以一次性创建10个线程,但同时也最多只能有10个线程同时进行,当有多的任务时,需等待,其中有线程空闲。(该例子是模拟投票,一次最多只能10个用户投票,只有投票结束后才能有新的用户投票,当然最多也只能10个用户。)该线程池的创建方法并不推荐,他只能创建固定大小的线程池。并不能设置其他参数。后续会补充推荐的创建线程池方法。

public static void main(String[] args) {
        // 创建固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        while (true){
            // 提交多个执行任务至线程池,并执行
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"投票!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

这有一个同时用四种方法创建四个线程的案例 

//        StringBuilder sb =new StringBuilder();
        StringBuffer sb =new StringBuffer();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                for (char c ='A';c<='E';c++){
                    sb.append(c);
                }
            }
        };
        Thread t2 =new Thread(new Runnable() {
            @Override
            public void run() {
                for (char i = 'a'; i <='e' ; i++) {
                    sb.append(i);
                }
            }
        });
        Thread t3 =new Thread(new FutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                for (int i = 1; i <=5 ; i++) {
                    sb.append(i);
                }
            return sb.toString();
            }
        }));

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                char[] array ={'!','@','#','$','%'};
                for (int i = 0; i <array.length ; i++) {
                    sb.append(array[i]);
                }
            }
        });
        executorService.shutdown();

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
        System.out.println(sb);

三 、线程的状态

在Java程序中,一个线程对象通过调用start()方法启动线程,并且在线程获取CPU时,自动执行run()方法。run()方法执行完毕,代表线程的生命周期结束。
在整个线程的生命周期中,线程的状态有以下6种:

  • New:新建状态,新创建的线程,此时尚未调用start()方法;
  • Runnable:运行状态,运行中的线程,已经调用start()方法,线程正在或即将执行run()方法;
  • Blocked:阻塞状态,运行中的线程,在等待竞争锁时,被阻塞,暂不执行;
  • Waiting:等待状态,运行中的线程,因为sleep()join()等方法调用,进入等待;
  • Timed Waiting:计时等待状态,运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;
  • Terminated:终止状态,线程已终止,因为run()方法执行完毕。

当线程启动后,它可以在Runnable、Blocked、Waiting和Timed Waiting这几个状态之间切换,直到最后变成Terminated状态,线程终止 

image.png

线程终止的原因有:

  • 线程正常终止:run()方法执行到return语句返回
  • 线程意外终止:run()方法因为未捕获的异常导致线程终止
  • 对某个线程的Thread实例调用stop()方法强制终止(宇宙超级无敌强烈不推荐) 

四、窗口抢票案例

这个案例主要关键是需要加synchronized
因为如果在三个窗口同时抢票的话
会出现同一时间卖出两张或者三张票通过不同窗口
而导致的问题是可能卖出三张票 票池中却只减少了1票
synchronized 的作用是 同一时间只能有一个线程访问票池 
当有线程人竞争到锁 其他线程只能堵塞

public class Main {
    public static void main(String[] args) {
        TickThread tickThread =new TickThread(20);
        new Thread(tickThread,"窗口1").start();
        new Thread(tickThread,"窗口2").start();
        new Thread(tickThread,"窗口3").start();
    }
}
class TickThread implements Runnable{
    //当前门票数
    private int tickCount;

    public TickThread(int tickCount) {
        this.tickCount = tickCount;
    }

    private final Object obj =new Object();

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"准备开始卖票喽!!");

        //线程竞争cpu执行权(this锁)
        synchronized (obj){
            while (true){
                if (tickCount<=0){
                    System.out.println(Thread.currentThread().getName()+"已经没有票了!");
                    return;
                }else {
                    System.out.println(Thread.currentThread().getName()+"卖出去了1张票,剩余"+ --tickCount+"张票!");
                    try {
                        //持有当前this锁的对象进入等待 1000毫秒
                        obj.wait(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    }
    public int getTickCount() {
        return tickCount;
    }

    public void setTickCount(int tickCount) {
        this.tickCount = tickCount;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值