【多线程】基础

1. 概念

1.1 使用多线程目的

提高系统的吞吐速度(提高CPU的利用率):充分利用多CUP多核(一个核可以对应多个线程数,如四核八线程),一个核对应一个线程。单核CPU时代,只能有一个线程利用CPU,使用多线程提高系统利用率不明显。

1.2 多任务多进程

过去单核CPU时代,计算机能在同一时间点并行执行多任务或多进程。并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一定的时间片运行。
而现在多核CPU的情况下,同一时间点可以执行多个任务,具体到这个任务在CPU哪个核上运行,这个就跟操作系统和CPU本身的设计相关了

1.3 程序线程数

比如说tomcat为每个请求启动一个线程处理,其中maxThreads设置为1000,有1000个用户并发,那么Tomcat就会起1000个线程来处理。那么假如实际CPU线程数只有30,1000个线程分时间片段使用30个CPU线程处理,从外部上看是同一时间。类似单核CUP多任务。

1.4如何设置程序线程数

(1)活跃线程数为 CPU(核)数时最佳。
过少的活跃线程导致 CPU 无法被充分利用,过多的活跃线程导致过大的线程上下文切换开销,同时更多的内存开销,更多的CPU开销。
活跃线程数:处于 IO 的线程,休眠的线程等均不消耗 CPU,不属于活跃线程。在实际环境中,当前活跃线程数一直在变化,很多活跃线程可能因为需要进行 IO 处理或等待资源而处于非活跃状态,假如说线程的数量等于 CPU(核)数,这就意味着活跃线程数小于 CPU(核)数。
(2)确定线程数的原则
提高CPU的中签率:多线程编程中,在确保内存不溢出的情况下提升线程数是可以提高CPU中签率的,也就是能提高你的程序处理数据的速度。
不是线程数越大越好:即使没有溢出,也不是线程数越大越好,线程切换毕竟需要时间,应该找到瓶颈所在
(3)依据cpu核数设置合适的线程数
获取cpu核心数:Runtime.getRuntime().availableProcessors();

// 根据CPU数量创建线程池
 private static ExecutorService printJobthreadPool = Executors
                        .newFixedThreadPool(Runtime.getRuntime().availableProcessors());

2. 线程安全

2.1 线程安全是由什么引起

线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;
若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。以下情况是线程安全的:

  • 常量始终是线程安全的,因为只存在读操作。
  • 每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
  • 局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。

2.2 解决线程安全的方式

为了解决多线程中相同变量的访问冲突问题,有两种方法:ThreadLocal和线程同步机制。
(1)同步机制
对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
(2)ThreadLocal
ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
ThreadLocal具体使用:

private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>()
//我们有时候会使用静态变量来存放某些值。但是并发情况下静态变量是不安全的。因此使用ThreadLocal创建独立的副本。
private static final ThreadLocal contextHolder=new ThreadLocal();

然后使用常用方法操作:
set:设置当前线程的线程局部变量的值。
get:返回当前线程所对应的线程局部变量。
remove:将当前线程局部变量的值删除。

(3) 什么是线程安全的类
线程安全的类 ,指的是类内共享的全局变量的访问必须保证是不受多线程形式影响的。如果由于多线程的访问(比如修改、遍历、查看)而使这些变量结构被破坏或者针对这些变量操作的原子性被破坏,则这个类就不是线程安全的。

2.4 无状态Bean

(1)无状态bean和有状态的定义

  • 无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 ,不能保存数据,是不变类,是线程安全的。无状态的Bean适合用不变模式,技术就是单例模式,这样可以共享实例,提高性能。
  • 有状态对象:有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。
package com.sw;  
 
public class TestManagerImpl implements TestManager{  
    private User user;    //实例变量
    public void deleteUser(User e) throws Exception {  
        user = e ;           //1  
        prepareData(e);  
    }  
    public void prepareData(User e) throws Exception {  
        user = getUserByID(e.getId());            //2  
        .....  
        //使用user.getId();                       //3  
        .....  
        .....  
    }     
}  

TestManagerImpl是一个有状态的Bean,如果该Bean配置为singleton,会出现什么样的状况呢?

3. 使用流程

多线程使用步骤主要有:

3.1 建立线程任务

实现方式主要有两种:实现Runnable接口和继承Thread类。实现Runnable接口相比继承Thread类有如下好处:

  • 避免继承的局限,一个类可以继承多个接口。
  • 适合于资源的共享。

在程序开发中只要是多线程肯定永远以实现Runnable接口为主。两种方式都要实现run()方法。
(1)实现Runnable接口
主要的实现步骤很简单:构造函数和实现run方法

public class RefundNotify implements Runnable
{
    private RefundNotifyMapper mRefundNotifyMapper;
    private LinkedBlockingQueue<String> queue;
    private String url;
    private static Logger logger = LoggerFactory.getLogger(AutoRepayServiceImpl.class);
    //构造函数
    public RefundNotify(LinkedBlockingQueue<String> queue, RefundNotifyMapper mRefundNotifyMapper, String url01)
    {
        System.out.println("退款通知线程开始工作!");
        this.queue = queue;
        this.mRefundNotifyMapper = mRefundNotifyMapper;
        this.url = url01;
    }
    // 实现run方法
    @SneakyThrows
    @Override
    public void run()
    {
    }   
}

Runnable里面没有start方法可以通过Thread类进行启动Runnable多线程,Runnable可以用同一个对象实例化的,可以资源共享,Thread不可以

//同一个实例
MyThreadWithImplements myRunnable = new MyThreadWithImplements();
hread thread1 = new Thread(myRunnable, "窗口一");
Thread thread2 = new Thread(myRunnable, "窗口二");
Thread thread3 = new Thread(myRunnable, "窗口三");
thread1.start();
thread2.start();
thread3.start();

(2)继承Thread类
使用不多,不详细介绍

3.2 创建线程池

使用ThreadPoolExecutor创建线程池并执行

//获取系统处理器个数,作为线程池数量
int nThreads = Runtime.getRuntime().availableProcessors();
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("member-pool-%d").build();
//创建线程池
ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
//传入任务,执行
pool.execute(refundNotify);
pool.execute(invoiceNotify);
pool.execute(timingRetryNotify);

3.3 线程池调度执行

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,593评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,080评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,025评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,317评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,329评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,036评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,639评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,557评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,089评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,197评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,330评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,006评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,689评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,189评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,313评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,676评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,339评论 2 358

推荐阅读更多精彩内容