Posts Thread
Post
Cancel

Thread

Thread

线程,作为一个运算单位,一个线程的代码运行计算顺序是一行一行从上到下的,当有一些程序或者计算任务需要能够同时运行多个,那么一个线程不能满足要求,多个线程才可以。

多个线程能并行执行计算,是其本意

java线程Thread

启动

创建一个Thread对象或作其子类,调用start()方法启动一个线程。

1
2
Thread thread = new Thread(); //创建线程对象
thread.start(); //启动

了解java线程的人都知道,启动线程得有一个Runnable实例用来运行run方法:

官方文档解释说,调用start()方法是线程开始执行,java虚拟机将调用此线程的run方法。

来看看Runnable和Thread有什么关联:

Thread的run方法

1
2
3
4
5
6
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

target又是什么?

看一下Thread的构造方法就知道是target就是Runnable接口的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
//构造函数
public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0); //线程初始化
}

//线程初始化方法
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
    //其中一行代码
    this.target = target; //传一个Runnable实例给target
    
}

所以要启动一个特定任务的线程,只需要实现Runnable接口,重写run方法即可

Runnable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class PeopleLife implements Runnable{
    private int age;
    @Override
    public void run(){
        while(age < 120){
            System.out.println("life in " + age);
            ++age;
        }
        //....
    }
    public static void main (String[] args){
        PeopleLife peopleLife = new PeopleLife(); //Runnable
        Thread peopleActivity = new Thread(peopleLife); //Runnable -> Thread
        peopleActivity.start();
    }
}

Concurrent

并发是短时间内发生很多事,处理并发事情不一定需要线程,但线程往往是较好的处理方式。

并发中用线程能减轻数据堵塞

从网络通信上来讲,并发就是短时间内收到多个通信,socket 套接字短时间内接受请求次数越多,并发量越大,每次socket通信是携带字节数据的,如果一个请求数据大小是1kb,1024个请求就是1Mb。

1s一次请求,你可以调用方法去处理数据,5次你也可以调用5次同样的方法去处理数据。但是1000次呢,你用调用方法1000次的方式去轮流处理数据吗,可能你刚处理1/10(100条)的数据花了1s钟(还不包括有些通信数据有误,具体逻辑处理可能失败等因素),这处理信息的1s内又来了1000条数据,此时有1900条数据缓存没有处理,这会造成堵塞。线程有一个特性:多个线程能够同时运行,用线程去做,通信信息的处理情况会好一些。

1000次方法和1000个线程的区别:

1000次方法执是按顺序一个个执行,上一个方法执行完,才能执行下一个方法。

1000个线程是随机并发运算,他们互不影响

CPU 的缓冲数据区是有限的,内存使用也是,10000条数据如果是10G的数据量,计算机内存只有8G,你要怎么处理,把2G的数据丢掉?

自然数据最宝贵,请求数据最好不丢失,为了能够在高并发的情况下处理每一条通信请求,首先应该把请求数据保存好,而不是去处理数据业务

线程不能无限用

并发中同时存在线程数有最大值,请求数或者任务数大于线程数阈值要有解决方案,可以先把业务数据保存下来

如何保存讲一下大概思想:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Public class DataCache implements Runnable,Serializable{
    
    private String requestData;
    
    public DataCache(String data){
        this.requestData = data;
    }
    
    @Override
    public void run(){
    	  dealWithData  
    }
    
    public void dealWithData(){
        if(requestData != null){
            //do something...
        }
    }
}

写一个类DataCache,用来缓存请求数据,支持线程处理,实现Serializable接口,支持序列化

如果是不确定的高并发,下面对应逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void runManyThread() {
		// 模拟10万个请求
		for(int i=0;i<100000;i++) {
			Runnable data = new DataCache("data: " + i);
            if(Runtime.getRuntime().freeMemory() > Threshold){
                //如果有充足内存等硬件资源,可以启动线程,处理业务
                Thread thread = new Thread(data);
                thread.start(); 
            }else {
                save(data); // 计算资源不够用,先把数据保存下来...
            }
			
		}	
	}

Runtime.getRuntime().freeMemory() 是java中的API,表示获取当前运行环境空闲内存大小,不管C还是java或其他语言,都有自己的方式获取内存信息。

上面只是说处理逻辑,具体代码肯定更详细。计算机资源够用,就运行线程,否则先保存数据,怎么保存具体情况具体分析,序列化可以是保存方案的一种。

内存使用达到瓶颈,再继续使用内存,导致内存溢出或者服务器卡顿程序退出,都是不可取的。

ThreadPool

线程池(线程集),就是很很多线程的集合,线程池的作用在于管理线程资源,集中处理,提高线程使用效率。

线程关联的是计算机的CPU资源,每个线程都是要占用CPU和内存资源的,所以线程的启动、运行、停止、销毁都对应的CPU的一系列动作,主要是对大量线程集中管理回收,较小系统开销

This post is licensed under CC BY 4.0 by the author.

关于博客

关于游戏和其未来可能性

Comments powered by Disqus.