线程一点都不难

什么是线程

线程是系统调度的最小单位,是轻量级的“进程”(因为创建和销毁一个线程比进程的成本低,因为线程之间可以共享资源,每次创建不需要分配过多的新资源)。进程相当于一个工厂,而线程相当于工厂中一个个流水线,也就是说一个进程可以包含多个线程。线程之间是相互独立的。线程虽然比进程轻量,但还不够轻量,而协程是线程还轻量的。go,Python等语言就内置了协程。

进程与线程之间的关系

  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位。
  • 一个进程内的线程之间是可以共享进程的资源的(主要共享内存资源和打开的文件,线程的上下文,状态,记账信息、优先级是不可以进行共享的,栈空间是每个线程要独立的一份),而进程不可以资源共享。
  • 每个进程可以有多个线程,至少有一个线程存在,即主线程。(创建一个进程的时候,至少会随之创建一个线程,即主线程)
  • 进程的实际执行单位就是线程
  • 线程的存储必须依赖于进程
  • 线程更轻量,比进程的并发效率更高
  • 调度进程的本质还是在调度线程
  • 一个进程中的线程并不是越多越好,一个进程中最多的线程数与CPU的个数和线程执行任务类型有关(CPU密集型(程序一直在执行计算任务)和io密集型(计算很少主要进行输入输出操作)),若为CPU密集型,理论上最多能创建CPU的个数个线程,io密集型理论上可以创建任意多个。

线程常见的一些属性

在这里插入图片描述

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

线程管理

线程管理是通过pcb来进行描述的,通过一个双向列表来进行组织的。内核只认识pcb,一个线程之和一个pcb对应,而一个进程可以和多个pcb对应。内核中把属于同一个进程的线程称为“线程组”,如果某几个pcb的的线程组ID是一样的,则这几个线程属于同一个进程。内核中并不区分谁是进程与线程,统一通过pcb进行管理,只要线程组ID一样,他们就属于同一个进程。一个线程只有执行了启动方法才会真正的被创建,创建对应的pcb。

线程休眠的方法

在这里插入图片描述

创建线程的放法

方法1-继承 Thread 类
可以通过继承 Thread 来创建一个线程类,该方法的好处是 this 代表的就是当前线程,不需要通过Thread.currentThread() 来获取当前线程的引用。

//直接用Thread类创建线程
public class ThreadDemo4 {
    public static void main(String[] args) {
        // 写法 2
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("当前线程名称:" +
                        Thread.currentThread().getName());
            }
        };
        // 运行线程
        thread.start();
    }
}


-------------------------------------------------------------------------------------------
public class ThreadDemo3 {
    // 1.继承 Thread 类
    static class MyThread extends Thread {
        @Override
        public void run() {
            // 写你的业务代码
            // 打印当前线程的名称
            System.out.println("子线程名称:" +
                    Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        // 1.继承 Thread 类
        MyThread myThread = new MyThread();
        // 启动线程
        myThread.start();

        System.out.println("主线程:" +
                Thread.currentThread().getName());

    }


}

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

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程名:" +
                Thread.currentThread().getName());
    }
}

public class ThreadDemo5 {
    public static void main(String[] args) {
        // 创建 Runnable 子对象
        MyRunnable myRunnable = new MyRunnable();
        // 创建线程
        Thread thread = new Thread(myRunnable);
        // 启动线程
        thread.start();
    }
}
-----------------------------------------------------------------------------------
public class ThreadDemo6 {
    public static void main(String[] args) {
        // 创建一个匿名 Runnable 类
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("当前线程:" +
                        Thread.currentThread().getName());
            }
        });
        thread.start();
    }
}
--------------------------------------------------------------------------------------

其他变形(了解)

// 使用匿名类创建 Thread 子类对象
Thread t1 = new Thread() {
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Thread 子类对象");
   }
};
// 使用匿名类创建 Runnable 子类对象
Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Runnable 子类对象");
   }
});
// 使用 lambda 表达式创建 Runnable 子类对象
Thread t3 = new Thread(() -> System.out.println("使用匿名类创建 Thread 子类对象"));
Thread t4 = new Thread(() -> {
    System.out.println("使用匿名类创建 Thread 子类对象");
});

创建一个有返回值的线程

package thread.thread0424;

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

/**
 * 创建一个有返回值的线程
 */
public class ThreadDemo8 {
    // 创建线程
    static class MyCallable implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            // 产生随机数
            int num = new Random().nextInt(10);
            System.out.println(String.format("线程:%s,生产了随机数:%d",
                    Thread.currentThread().getName(), num));
            return num;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.创建 Callable 子对象
        MyCallable callable = new MyCallable();
        // 2.使用 FutrueTask 接收 Callable
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        // 3.创建线程并设置任务
        Thread thread = new Thread(futureTask);
        // 执行线程
        thread.start();
        // 得到线程的执行结果
        int num = futureTask.get();
        System.out.println("线程返回结果:" + num);
    }
}

线程分组

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

        ThreadGroup threadGroup = new ThreadGroup("group1");

        Thread t1 = new Thread(threadGroup, new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    System.out.println("选手1达到终点了~");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();

        Thread t2 = new Thread(threadGroup, new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1200);
                    System.out.println("选手2达到终点了~");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.start();

        // 等待所有人员到达终点
        while (threadGroup.activeCount() != 0) {
        }

        System.out.println("宣布比赛结果");
    }
}

线程的常见属性

/**
 * 演示线程的常见属性
 */
public class ThreadDemo11 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread t = Thread.currentThread();
                System.out.println("线程ID:" + t.getId());
                System.out.println("线程名称:" + t.getName());
                System.out.println("线程状态:" + t.getState());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1");
        // 启动线程
        t1.start();
        // 打印线程状态
        Thread.sleep(100);
        System.out.println("t1 线程状态:" + t1.getState());
        System.out.println("优先级:" + t1.getPriority());
        System.out.println("是否为守护线程:" + t1.isDaemon());
        System.out.println("是否存活:" + t1.isAlive());
        System.out.println("是否被中断:" + t1.isInterrupted());

        // 等待线程执行完
        t1.join(); // 方式一
        while (!t1.isAlive()) { // 方式二
        }

    }
}

带优先级的线程

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

        for (int i = 0; i < 10; i++) {
            // 权重最低的线程
            Thread t1 = new Thread(() -> {
                System.out.println("t1");
            }, "t1");
            // 线程一优先级最低
            t1.setPriority(1);
            t1.start();
            // 权重最高的线程
            Thread t2 = new Thread(() -> {
                System.out.println("t2");
            }, "t2");
            // 线程一优先级最低
            t2.setPriority(5);
            t2.start();
            // 权重最高的线程
            Thread t3 = new Thread(() -> {
                System.out.println("t3");
            }, "t3");
            // 线程一优先级最低
            t3.setPriority(10);
            t3.start();
        }

    }
}

守护线程
守护线程的使用场景有1、java垃圾回收器,监控监测任务(比如tcp的保活机制)

注意事项

  • 守护线程的设置必须放在start之前
  • 守护线程内创建的线程也是守护线程
public class ThreadDemo13 {
    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("i:" + i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 设置后台线程(守护线程)
        t1.setDaemon(true);
        t1.start();
        System.out.println("线程类型:" + t1.isDaemon());
    }
}

run() vs start():

1.run() 方法是一个对象的普通方法,它使用的是主线程来执行任务的。
2.start()是线程的开启方法,它使用新的线程来执行任务的。
3.start()方法只能执行一次,而run()可以调用n次。

线程中断的方式

1.自定义全局标识来实现中断,比较温柔的方式,不会立马终止,而是等待当前的一次任务执行完在终止。

// 全局变量
    private static boolean flag = false;

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            while (!flag) {
                System.out.println("我正在转账...");
//                try {
//                    // 休眠线程
//                    Thread.sleep(100);
//                    System.out.println("我正在转账...");
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                    break;
//                }
            }
            System.out.println("啊?差点误了大事。");
        }, "张三");
        // 开启任务
        t1.start();

        // 休眠主线程一段时间
        Thread.sleep(310);

        // 终止线程
        System.out.println("停止交易,有内鬼.");
        flag = true;
    }

2.使用Thread的intrrupted来中断。使用系统的 Intrruput()可以及时立马的终止线程,比较暴力。


 public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            while (!Thread.interrupted()) {
//另一种判断线程终止的方法
//while ( ! Thread.currentThread().isInterrupted()) {

                try {
                    // 休眠线程
                    Thread.sleep(100);
                    System.out.println("我正在转账...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
            System.out.println("啊?差点误了大事。");
        }, "张三");
        // 开启任务
        t1.start();

        // 休眠主线程一段时间
        Thread.sleep(310);

        // 终止线程
        System.out.println("停止交易,有内鬼.");
        t1.interrupt();

    }

静态方法Thread.interrupted():第一次接收到终止状态是true,之后就会将状态复位(恢复成false),因为是全局的,可能会被好多人使用 。普通方法 .isInterrupted()只用来得到线程的状态,并不会进行复位。因为是私有的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值