Java多线程系列(1)

Wesley13
• 阅读 657

本章主要内容有:

1.线程进程的区别

2.线程的生命周期

3.Java内存模型 原子性,可见性及有序性

4.线程池及Java实现

1. 线程进程的区别

线程:程序运行的最小单位
进程:资源分配的最小单位
一个进程可以有多个线程,多个线程共享进程里面的数据
线程间通讯相对更加方便,进程间通讯需要通过IPC(Inter-Process Communication,进程间通信)实现

JVM运行时数据区域是否线程共享:
程序计数器-线程私有
虚拟机栈-线程私有
本地方法栈-线程私有
堆-线程共享
方法区-线程共享

  3. 线程的生命周期

Java多线程系列(1)

1.sleep()
在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。
sleep()使当前线程进入等待状态,在指定时间内不会执行。
2.wait()
在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。
3.yield()
暂停当前正在执行的线程对象。
yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
yield()只能使同优先级或更高优先级的线程有执行的机会。 
调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
4.join()
等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。

  Java多线程系列(1)

4. Java线程几种创建方式

1. 实现Runnable或Callable接口
2. 实现Thread类 
3. 通过线程池启动1,2创建的线程

5. Java内存模型(JMM)

Java内存模型定义了线程和内存的交互方式,在JMM抽象模型中,分为主内存、工作内存;
主内存是所有线程共享的,一般是实例对象、静态字段、数组对象等存储在堆内存中的变量。
工作内存是每个线程独占的,线程对变量的所有操作都必须在工作内存中进行,
不能直接读写主内存中的变量,线程之间的共享变量值的传递都是基于主内存来完成。

  Java多线程系列(1)

  1. JMM中的8个原子操作

    lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。 unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。 read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用 load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。 use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。 assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。 store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。 write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

  Java多线程系列(1)

  1. Happen-before规则

    Happens-before原则定义:

    1. 如果A操作happens-before B操作,那么A操作的执行结果将对B操作可见,而且A操作的执行顺序排在B操作之前。
    2. 两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。 Happens-before规则(摘自《深入理解Java虚拟机第12章》):

    1.程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作; 2.锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作; 3.volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作; 4.传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C; 5.线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作; 6.线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生; 7.线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行; 8.对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;

    例:A线程调用set, B线程调用同一个对象的get,那么B线程返回什么? private int i = 0; public void set(int j ){ i = j; } public int get(){ return i; }

    如何修改满足Happen-before规则?

6. 原子性,可见性,有序性特征

原子性
即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。原子性就像数据库里面的事务一样,他们是一个团队,同生共死
可见性
是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
有序性
即程序执行的顺序按照代码的先后顺序执行
解决方案:

原子性:
Java中提供了两个高级指令 monitorenter和 monitorexit,也就是对应的synchronized同步锁来保证原子性,也可以通过lock(),unlock()对保证代码块的原子性
可见性:
volatile、synchronized、final都可以解决可见性问题
有序性:
synchronized和volatile可以保证多线程之间操作的有序性,volatile会禁止指令重排序

7. 线程池及Java实现

线程池及为什么使用线程池

在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程 
  第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 
  第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。 
  第三:提高线程的可管理性。

Java线程池的参数

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
               BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
          RejectedExecutionHandler handler)
1.corePoolSize:  核心池的大小。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
2.maximumPoolSize:  线程池最大线程数。这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
3.keepAliveTime:  表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
4.unit:  参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性
5.workQueue:  一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:ArrayBlockingQueue; LinkedBlockingQueue; SynchronousQueue; PriorityBlockingQueue
ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和SynchronousQueue。线程池的排队策略与BlockingQueue有关。
6.threadFactory:  线程工厂,主要用来创建线程
7.handler:  表示当拒绝处理任务时的策略。有四种取值:
      AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;DiscardPolicy:也是丢弃任务,但是不抛出异常
      DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程);CallerRunsPolicy:由调用线程处理该任务。

线程在线程池中的处理:
1、如果线程池的当前大小还没有达到基本大小(poolSize < corePoolSize),那么就新增加一个线程处理新提交的任务;

2、如果当前大小已经达到了基本大小,就将新提交的任务提交到阻塞队列排队,等候处理workQueue.offer(command);

3、如果队列容量已达上限,并且当前大小poolSize没有达到maximumPoolSize,那么就新增线程来处理任务;

4、如果队列已满,并且当前线程数目也已经达到上限,那么意味着线程池的处理能力已经达到了极限,此时需要拒绝新增加的任务。至于如何拒绝处理新增

Java线程池的实现

Executors.newFixedThreadPool(8)
  Executors.newCachedThreadPool()
  Executors.newSingleThreadExecutor()
  Executors.newScheduledThreadPool(8)
  Executors.newWorkStealingPool()
  Executors.newSingleThreadScheduledExecutor()
详见:https://www.cnblogs.com/barrywxx/p/8570558.html

问题:Executors.newCachedThreadPool()核心线程数设置为0,如何创建销毁线程的?
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
DevOpSec DevOpSec
3年前
python多线程原理和详解(一)
python多线程原理和详解线程概念1.线程是什么?线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
Wesley13 Wesley13
3年前
java线程
1.进程和线程的区别是什么?进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫轻量级进程。2.创建线程有几种不同方式?你喜欢哪种?为什么?有三种方式可以用来创建线程:继承Thread类实现Runnable接口应用程序可以使用Executor框架来创建线程池实现Runnabl
Wesley13 Wesley13
3年前
java多线程面试题_线程并发面试题
1、什么是线程?线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。2、线程和进程有什么区别?线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不
Wesley13 Wesley13
3年前
4、jstack查看线程栈信息
1、介绍利用jps、top、jstack命令找到进程中耗时最大的线程,以及线程状态等等,同时最后还可以显示出死锁的线程查找:FoundoneJavaleveldeadlock即可1、jps获得进程号!(https://oscimg.oschina.net/oscnet/da00a309fa6
Stella981 Stella981
3年前
Noark入门之线程模型
0x00单线程多进程单线程与单进程多线程的目的都是想尽可能的利用CPU,减少CPU的空闲时间,特别是多核环境,今天咱不做深度解读,跳过...0x01线程池锁最早的一部分游戏服务器是采用线程池的方式来处理玩家的业务请求,以达最大限度的利用多核优势来提高处理业务能力。但线程池同时也带来了并发问题,为了解决同一玩家多个业务请求不被
Stella981 Stella981
3年前
Linux 多线程编程
1.Linux“线程”进程与线程之间是有区别的,不过Linux内核只提供了轻量进程的支持,未实现线程模型。Linux是一种“多进程单线程”的操作系统。Linux本身只有进程的概念,而其所谓的“线程”本质上在内核里仍然是进程。大家知道,进程是资源分配的单位,同一进程中的多个线程共享该进程的资源(如作为共享内存的全局变量)。Linux
Wesley13 Wesley13
3年前
Java线程与多线程
1线程与多线程1.1线程是什么?线程(Thread)是一个对象(Object)。用来干什么?Java线程(也称JVM线程)是Java进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。Java程序采用多线程方式来支持大量的并发请求处理,程序如果在
深入浅出线程池 | 京东云技术团队
一、线程1、什么是线程线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。2、如何创建线程2.1、JAVA中