Java多线程

用多线程只有一个目的,就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。

多线程:指的是这个程序(一个进程)运行时产生了不止一个线程

并行与并发:

  • 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
  • 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。
  • 线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:

void transferMoney(User from, User to, float amount){
    to.setMoney(to.getBalance() + amount);
    from.setMoney(from.getBalance() - amount);
}

同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。

1.
线程的状态

"Blocked"和"Waiting"这两个状态的区别:

线程在Running的过程中可能会遇到阻塞(Blocked)情况
对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。从jdk源码注释来看,blocked指的是对monitor的等待(可以参考下文的图)即该线程位于等待区。

线程在Running的过程中可能会遇到等待(Waiting)情况
线程可以主动调用object.wait或者sleep,或者join(join内部调用的是sleep,所以可看成sleep的一种)进入。从jdk源码注释来看,waiting是等待另一个线程完成某一个操作,如join等待另一个完成执行,object.wait()等待object.notify()方法执行。

Waiting状态和Blocked状态有点费解,我个人的理解是:Blocked其实也是一种wait,等待的是monitor,但是和Waiting状态不一样,举个例子,有三个线程进入了同步块,其中两个调用了object.wait(),进入了waiting状态,这时第三个调用了object.notifyAll(),这时候前两个线程就一个转移到了Runnable,一个转移到了Blocked。

从下文的monitor结构图来区别:每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是
“Waiting Thread”,分别在两个队列 “ Entry Set”和
“Wait Set”里面等候。在 “Entry Set”中等待的线程状态Blocked,从jstack的dump中来看是
“Waiting for monitor entry”,而在 “Wait
Set”中等待的线程状态是Waiting,表现在jstack的dump中是 “in Object.wait()”。

此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。

2. 每个对象都有的方法(机制)

synchronized, wait, notify 是任何对象都具有的同步工具。

他们是应用于同步问题的人工线程调度工具。Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。

wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。

synchronized单独使用:

代码块:如下,在多线程环境下,synchronized块中的方法获取了lock实例的monitor,如果实例相同,那么只有一个线程能执行该块内容

public class Thread1 implements Runnable {
        Object lock;
        public void run() {  
            synchronized(lock){
              ..do something
            }
        }
}

直接用于方法: 相当于上面代码中用lock来锁定的效果,实际获取的是Thread1类的monitor。更进一步,如果修饰的是static方法,则锁定该类所有实例。

public class Thread1 implements Runnable {
        public synchronized void run() {  
             ..do something
        }
}

synchronized, wait, notify结合:典型场景生产者消费者问题

/**
     * 生产者生产出来的产品交给店员
     */
    public synchronized void produce()
    {
        if(this.product >= MAX_PRODUCT)
        {
            try
            {
                wait();  
                System.out.println("产品已满,请稍候再生产");
            }
            catch(InterruptedException e)
            {
                e.printStackTrace();
            }
            return;
        }

this.product++;
        System.out.println("生产者生产第" + this.product + "个产品.");
        notifyAll();   //通知等待区的消费者可以取出产品了
    }

/**
     * 消费者从店员取产品
     */
    public synchronized void consume()
    {
        if(this.product <= MIN_PRODUCT)
        {
            try 
            {
                wait(); 
                System.out.println("缺货,稍候再取");
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
            return;
        }

System.out.println("消费者取走了第" + this.product + "个产品.");
        this.product--;
        notifyAll();   //通知等待去的生产者可以生产产品了
    }

3. 基本线程类

基本线程类指的是Thread类,Runnable接口,Callable接口

4. 高级多线程控制类

.ThreadLocal类

用处:保存线程的独立变量。对一个线程类(继承自Thread)
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。常用于用户登录控制,如记录session信息。

实现:每个Thread都持有一个TreadLocalMap类型的变量(该类是一个轻量级的Map,功能与map一样,区别是桶里放的是entry而不是entry的链表。功能还是一个map。)以本身为key,以目标为value。

主要方法是get()和set(T
a),set之后在map里维护一个threadLocal -> a,get时将a返回。ThreadLocal是一个特殊的容器。

2.原子类(AtomicInteger、AtomicBoolean……)

如果使用atomic wrapper class如atomicInteger,或者使用自己保证原子的操作,则等同于synchronized

//返回值为boolean
AtomicInteger.compareAndSet(int expect,int update)

3.Lock类

ReentrantLock 可重入的意义在于持有锁的线程可以继续持有,并且要释放对等的次数后才真正释放该锁。

使用方法是:

1.先new一个实例

static ReentrantLock r=new ReentrantLock();

2.加锁

r.lock()或r.lockInterruptibly();

此处也是个不同,后者可被打断。当a线程lock后,b线程阻塞,此时如果是lockInterruptibly,那么在调用b.interrupt()之后,b线程退出阻塞,并放弃对资源的争抢,进入catch块。(如果使用后者,必须throw interruptable
exception 或catch)

3.释放锁

r.unlock()

必须做!何为必须做呢,要放在finally里面。以防止异常跳出了正常流程,导致灾难。这里补充一个小知识点,finally是可以信任的:经过测试,哪怕是发生了OutofMemoryError,finally块中的语句执行也能够得到保证。

ReentrantReadWriteLock

可重入读写锁(读写锁的一个实现)

 ReentrantReadWriteLock lock = new ReentrantReadWriteLock()
  ReadLock r = lock.readLock();
  WriteLock w = lock.writeLock();

两者都有lock,unlock方法。写写,写读互斥;读读不互斥。可以实现并发读的高效线程安全代码

阅读之Java多线程的更多相关文章

  1. 一起阅读《Java多线程编程核心技术》

    目录 第一章 Java多线程技能 (待续...)

  2. 《Java 多线程编程核心技术》- 笔记

    作为业务开发人员,能够在工作中用到的技术其实不多.虽然平时老是说什么,多线程,并发,注入,攻击!但是在实际工作中,这些东西不见得用得上.因为,我们用的框架已经把这些事做掉了. 比如web开发,外面有大 ...

  3. Thread.currentThread()和this的区别——《Java多线程编程核心技术》

    前言:在阅读<Java多线程编程核心技术>过程中,对书中程序代码Thread.currentThread()与this的区别有点混淆,这里记录下来,加深印象与理解. 具体代码如下: pub ...

  4. 《Java多线程编程实战指南(核心篇)》阅读笔记

    <Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...

  5. java 多线程 1 线程 进程

    Java多线程(一).多线程的基本概念和使用 2012-09-10 16:06 5108人阅读 评论(0) 收藏 举报  分类: javaSE综合知识点(14)  版权声明:本文为博主原创文章,未经博 ...

  6. Java多线程学习笔记

    进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...

  7. Java多线程系列--“基础篇”04之 synchronized关键字

    概要 本章,会对synchronized关键字进行介绍.涉及到的内容包括:1. synchronized原理2. synchronized基本规则3. synchronized方法 和 synchro ...

  8. JAVA多线程和并发基础面试问答(转载)

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  9. Java多线程学习(转载)

    Java多线程学习(转载) 时间:2015-03-14 13:53:14      阅读:137413      评论:4      收藏:3      [点我收藏+] 转载 :http://blog ...

随机推荐

  1. C学习笔记-makefile

    除非最简单的项目,任何一个商业项目都会包含多个源代码,而且在编译的时候会有很长很复杂的指令,为了编译的简单可行,makefile由此而生,在编译时候直接输入make便会按照makefile里面的规则编 ...

  2. 记录Linq中lambda动态表达式的使用方式

    项目中有的时候我们会用到动态表达式的方式去查询数据,这里简单记录下个人的使用方式,方便使用↓ //构建参数表达式 ParameterExpression parameter = Expression. ...

  3. linux静态IP配置和网关配置

    我们在配置CentOS的时候,很多情况需要能联外网,那么就需要DNS解析功能,默认的是没有配置DNS信息的,所以我们得配置DNS信息起因我们在搜索Centos配置DNS信息的时候,很多都是说在这个文件 ...

  4. 第六次java实验报告

    Java实验报告 班级 计科二班 学号20188437 姓名 何磊 完成时间 2019/10/17 评分等级 实验四 类的继承 实验目的 理解异常的基本概念: 掌握异常处理方法及熟悉常见异常的捕获方法 ...

  5. // TODO Auto-generated method stub 作用及设置

    当我们创建一个主方法类时,eclipse会默认插入一条注释语句: // TODO Auto-generated method stub 作用: // TODO Auto-generated metho ...

  6. Linux在线安装MongoDB

    Mongo官网安装地址: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/ 以MongoDB3.4为例 1.创建 ...

  7. 从入门到自闭之Python时间模块

    time模块:import time time.time():时间戳,是一个浮点数,按秒来计算 time.sleep():睡眠,程序暂停多少秒执行 python中时间日期格式化符号: 必背 %y 两位 ...

  8. redis 学习(8)-- redis 客户端 -- Jedis

    redis 客户端 -- Jedis 1. Jedis 直连 本质是 TCP 连接. 执行流程 创建Jedis对象 通过Jedis执行命令 返回Jedis执行结果 关闭Jedis连接 demo 要使用 ...

  9. Abp添加新的Api(不扩展底层方法)

    定义新的实体类:FileManage;继承 FullAuditedEntity<Guid> 在XX.Application 中定义IXXservice及实现XXservice public ...

  10. openlayers之地图截图

    方法1 //this.map._this为初始化地图对象 this.map._this.once('postcompose', function (event) { var canvas = even ...