java静态方法实现排队机,Java 多线程看这一片就够了

@[toc]

java 多线程

线程创建两种方式

集成Thread 类

实现Runable接口

两种方式都需要重写run方法

启动线程调用start()方法

创建线程

这里继承Thread 创建线程实例

public class ThreadStart {

/**

* java 应用程序的main函数是一个线程,是被jvm启动的时候调用,线程名字叫main

*

* 实现一个线程,必须创建Thread实例,重写 run方法,并且调用start方法

* 在jvm启动后,实际上有多个线程,但是至少有一个非守护线程 main 入口启动线程

* 当调用一个线程启动start方法的时候, 此时至少用两个线程,一个是调用你的线程 还是有一个是执行run的线程

*

* 线程的生命周期 分为 new Thread,runnbale,running,bloic,terminated

*

* 注意:

* start 之后不可能立即进入 running 先处于 runnbale

* bloic 之后 必须先回到 runnbale 再回到 running,过程中可能挂掉 处于 terminated

*

*/

public static void main(String[] args) {

//main方法线程名称

System.out.println(Thread.currentThread().getName());

Thread t1 = new Thread(){

@Override

public void run() {

//t1线程名称

System.out.println(Thread.currentThread().getName());

}

};

//启动线程 不是run方法 在源码中 将逻辑方法都抽象到run方法中,在start方法中 启动run方法,这样子可以通过重写来定义自己的业务逻辑

t1.start();

}

}

模拟排队机叫号排队

创建线程

public class ThreadBank1 {

public static void main(String[] args) {

BnakThread t1 = new BnakThread();

BnakThread t2 = new BnakThread();

BnakThread t3 = new BnakThread();

//启动

t1.start();

t2.start();

t3.start();

}

}

class BnakThread extends Thread {

int num = 1;

int max = 50;

@Override

public void run() {

while (num<=max){

System.out.println(Thread.currentThread() + " 的号码是 " + num );

num++;

}

}

}

结果如下

仅一部分数据

2c72d4286ada

仅一部分

这样虽然启动线程打印出结果,但是存在一个问题,这样三个排队机 互相各玩各的,三个排队机都将50个号码打印一个遍。

数据没有做到共享

解决办法1:

可以设置静态变量

static int num = 1;

static int max = 50;

可以使用runnable接口将逻辑从线程中分离出来

也就是创建现成的第二种方式实现Runable接口

定义一个类实现runnable接口

package com.company;

/**

* @description: 模拟叫号排队

* @author: Administrator

* @create: 2019-12-08 13:32

**/

public class ThreadRunnableBank2 {

public static void main(String[] args) {

BnakRunable runable = new BnakRunable();

Thread t1 = new Thread(runable,"1号");

Thread t2 = new Thread(runable,"2号");

Thread t3 = new Thread(runable,"3号");

//start

t1.start();

t2.start();

t3.start();

}

}

class BnakRunable implements Runnable {

static int num = 1;

static int max = 50;

@Override

public void run() {

while (num<=max){

System.out.println(Thread.currentThread() + " 的号码是 " + num++ );

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

可以看到结果如下

Thread[1号,5,main] 的号码是 1

Thread[3号,5,main] 的号码是 2

Thread[2号,5,main] 的号码是 3

Thread[1号,5,main] 的号码是 4

Thread[3号,5,main] 的号码是 4

Thread[2号,5,main] 的号码是 5

Thread[1号,5,main] 的号码是 6

Thread[3号,5,main] 的号码是 7

Thread[2号,5,main] 的号码是 8

Thread[3号,5,main] 的号码是 9

Thread[1号,5,main] 的号码是 9

Thread[2号,5,main] 的号码是 10

Thread[1号,5,main] 的号码是 11

Thread[3号,5,main] 的号码是 12

Thread[2号,5,main] 的号码是 13

Thread[3号,5,main] 的号码是 14

Thread[1号,5,main] 的号码是 15

Thread[2号,5,main] 的号码是 16

Thread[3号,5,main] 的号码是 17

Thread[1号,5,main] 的号码是 17

Thread[2号,5,main] 的号码是 18

Thread[1号,5,main] 的号码是 19

Thread[3号,5,main] 的号码是 20

Thread[2号,5,main] 的号码是 21

Thread[3号,5,main] 的号码是 22

Thread[1号,5,main] 的号码是 23

Thread[2号,5,main] 的号码是 24

Thread[3号,5,main] 的号码是 25

Thread[1号,5,main] 的号码是 25

Thread[2号,5,main] 的号码是 26

Thread[3号,5,main] 的号码是 27

Thread[1号,5,main] 的号码是 27

Thread[2号,5,main] 的号码是 28

Thread[3号,5,main] 的号码是 29

Thread[1号,5,main] 的号码是 29

Thread[2号,5,main] 的号码是 30

Thread[1号,5,main] 的号码是 31

Thread[3号,5,main] 的号码是 31

Thread[2号,5,main] 的号码是 32

Thread[1号,5,main] 的号码是 33

Thread[3号,5,main] 的号码是 33

Thread[2号,5,main] 的号码是 34

Thread[3号,5,main] 的号码是 35

Thread[1号,5,main] 的号码是 36

Thread[2号,5,main] 的号码是 37

Thread[3号,5,main] 的号码是 38

Thread[1号,5,main] 的号码是 38

Thread[2号,5,main] 的号码是 39

Thread[3号,5,main] 的号码是 41

Thread[1号,5,main] 的号码是 40

Thread[2号,5,main] 的号码是 42

Thread[3号,5,main] 的号码是 44

Thread[1号,5,main] 的号码是 43

Thread[2号,5,main] 的号码是 45

Thread[3号,5,main] 的号码是 46

Thread[1号,5,main] 的号码是 46

Thread[2号,5,main] 的号码是 47

Thread[1号,5,main] 的号码是 48

Thread[3号,5,main] 的号码是 48

Thread[2号,5,main] 的号码是 49

Thread[1号,5,main] 的号码是 50

Thread[3号,5,main] 的号码是 50

Runable的作用是将可执行的代码逻辑从线程中分离出来,这比较符合我们的面向对象思想。

线程生命周期

线程生命周期图例如下图

2c72d4286ada

图片来自网络

大致分为以下及格过程

1 new Thread() 创建线程

2 然后调用start()方法进入 runnable可执行状态,runbale也可能出现异常线程进入terminated 状态

3 可执行状态阶段争抢cpu ,抢到了cpu执行调度权进入running状态,如果没有抢到执行cpu 线程继续处于runbale状态

4 running过程中 可能存在 wait , sleep 等 让线程处于bolck状态

bolck 状态结束后 ,线程进入可执行 runnable 状态,然后 runnable 继续执行第3阶段步骤;

也可能出现异常,进入 terminated 死亡结束状态;

线程中一些常用方法

创建对象Thread的时候,默认会有一个线程名,以Thread-开头,从0开始计数

构造方法

new Thread();

Thread-0

Thread-1

Thread-2

public static void main(String[] args) {

Thread t0 = new Thread();

System.out.println(t0.getName());

Thread t1 = new Thread(){

@Override

public void run() {

//t1线程名称

Thread.currentThread().setName("线程2");

System.out.println(Thread.currentThread().getName());

}

};

t1.start();

t0.start();

}

可以看到默认是以0开头的

2c72d4286ada

在这里插入图片描述

通过源码可以看到具体缘由

"Thread-" + nextThreadNum() Thread开头 然后+ nextThreadNum()方法

public Thread() {

init(null, null, "Thread-" + nextThreadNum(), 0);

}

nextThreadNum() 方法 静态方法提供递增操作

private static synchronized int nextThreadNum() {

return threadInitNumber++;

}

也可以看到当构造方法里什么都不传值的话 调用默认的init初始化方法

Runable 参数传值null

init(null, null, "Thread-" + nextThreadNum(), 0);

Runable 赋值成员变量

2c72d4286ada

在这里插入图片描述

然后start开启线程执行run方法

调用本地方法

2c72d4286ada

在这里插入图片描述

本地run执行方法内容,可以看到 上面穿的值默认为null 所以没有任何线程内容输出。

2c72d4286ada

在这里插入图片描述

所以定义线程时候,要重写run方法实现自己业务逻辑

ThreadGroup 值,默认传值为null

如果没有传入 ThreadGroup 值,默认传值为null

2c72d4286ada

在这里插入图片描述

会获取父线程的ThreadGroup 作为该线程的ThreadGroup ,此时子线程和父线程将会在同一个ThreadGroup 里。

可以通过ThreadGroup 查看到 线程的运行数量等

//t1线程ThreadGroup名

System.out.println(t1.getThreadGroup().getName()); //main

//main 的ThreadGroup名

System.out.println(Thread.currentThread().getThreadGroup().getName());//main

获取运行线程数量

//获取到 ThreadGroup 中有多少个线程在运行

System.out.println(Thread.currentThread().getThreadGroup().activeCount()); //理想情况下是2个

参数 stackSize

stackSize 默认是0 表示跟平台有关,个人电脑配置内存大小和jvm内存大小有关

stackSize 参数表示意思是当前线程虚拟机栈帧大小

方法在调用的时候当前栈的大小决定方法可以进行压栈进栈多少次,如果超过这个次数会抛出 StackOverflowError 错误信息

通常默认情况下main方法栈帧,也就是jvm启动时候创建的栈帧大小即虚拟机栈

//计数器

private static int count;

/**

* 线程名

* @param args

*/

public static void main(String[] args) {

//main方法是jvm启动时候创建的 虚拟机栈帧大小是 49799

//main方法 中调用add方法压栈进栈 次数为 49799 次

try {

add(1);

}catch (Error e){

e.getMessage();

System.out.println(count);

}

}

/**

* 递归调用

* @param i

*/

static void add(int i){

++count;

add(i+1);

}

结果

java.lang.StackOverflowError

49799

线程设置栈帧大小

根据构造函数传参

2c72d4286ada

>

线程设置栈帧大小

public static void main(String[] args) {

Thread thread = new Thread(null, new Runnable() {

@Override

public void run() {

try {

add(1);

}catch (Error e){

System.out.println(e.getClass().getName());

System.out.println(count);

}

}

},"test",1<<24); // 栈帧 1<<24 次

thread.start();

}

/**

* 递归调用

* @param i

*/

static void add(int i){

++count;

add(i+1);

}

}

结果

java.lang.StackOverflowError

418012

设置守护线程

/**

* 守护线程 随着父线程结束而结束

* @param args

*/

public static void main(String[] args) {

Thread thread = new Thread(null, new Runnable() {

@Override

public void run() {

System.out.println("test 线程 ");

}

},"test");

/**

* 设置为true时候 控制台只打印main test 线程随着main线程结束而退出

* 设置为true时候 "test 线程 " 会打印

*/

thread.setDaemon(false); //默认是false

thread.start();

System.out.println("mian ");

}

结果

2c72d4286ada

在这里插入图片描述

设置守护线程时候 必须在启动线程之前设置,不然会抛出异常 java.lang.IllegalThreadStateException

线程优先级

void setPriority(int newPriority)

newPriority 默认是5 范围 1- 10最高级10和最低级1 ; 一般情况下 数字越大优先级越高

线程id

System.out.println(Thread.currentThread().getId()); //1 main线程由jvm 优先启动

System.out.println(thread2.getId()); //11

System.out.println(thread.getId()); //12

源码

public long getId() {

return tid;

}

2c72d4286ada

在这里插入图片描述

获取下一个线程id 值递增

private static synchronized long nextThreadID() {

return ++threadSeqNumber;

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值