Java并发编程系列(一)基础概念

1.线程状态图:

5种状态:

1.新建(new)

2.就绪(Runnable)

3.运行(Running)

4.阻塞(Blocked)

  • 等待阻塞:调用了wait()方法
  • 同步阻塞:调用了synchronized()方法或执行了synchronized代码块,但是没有获取到对象的同步锁。
  • 其他阻塞:调用sleep()或join()方法

5.死亡(Dead)

相关方法:

1.run():

是Runnable接口里唯一的一个抽象方法,可以被重复调用。直接调用run方法,并不会启动新线程,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到多线程的目的。总结:run方法只是thread的一个普通方法调用,还是在主线程里执行。


2.start():

启动一个新线程。通过start()方法来启动的新线程,使线程处于就绪(可运行)状态,一旦得到cpu时间片,就开始执行相应线程的run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。start()不能被重复调用。用start方法来启动线程,真正实现了多线程运行,即无需等待某个线程的run方法体代码执行完毕就直接继续执行下面的代码。

3.start()和run()的区别:

特性 start() run()
启动一个新线程 不能
重复调用 不能
是否实现多线程

4.wait()/notify()/notifyAll()

这三个都是Object()类的方法:

wait():让当前线程处于“等待(阻塞,挂起)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。

notify():唤醒在此对象监视器上等待的单个线程。

notifyAll():唤醒在此对象监视器上等待的所有线程。

Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。

wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行!
OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。现在,请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?答案是:依据“对象的同步锁”。

负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才能获取到“对象的同步锁”进而继续运行。

总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。

5.yield():

yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行!

yield()和wait()的区别:

wait()是让线程由“运行状态”进入到“等待(阻塞)状态”,而不yield()是让线程由“运行状态”进入到“就绪状态”。
wait()是会线程释放它所持有对象的同步锁,而yield()方法不会释放锁。

6.sleep():

sleep() 定义在Thread.java中。
sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。

sleep()和wait()的区别:

wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”,同时也会释放同步锁。而sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”。
但是,wait()会释放对象的同步锁,而sleep()则不会释放锁。

7.join():

join()使一个子线程加入一个主线程,并且让“主线程”等待“子线程”结束之后才能继续运行。

8.interrupt():中断一个线程

2.synchronized关键字的用法

第一条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
第二条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块
第三条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

主要分为:

1.synchronized方法

2.synchronized块:synchronized(this):获取当前对象的同步锁/synchronized(obj):获取某一个特定对象的对象锁

时间: 2024-11-10 07:12:26

Java并发编程系列(一)基础概念的相关文章

Java并发编程系列(一)-线程的基本使用

最近在学习java并发编程基础.一切从简,以能理解概念为主. 并发编程肯定绕不过线程.这是最基础的. 那么就从在java中,如何使用线程开始. 继承Thread类 继承Thread类,重写run方法,new出对象,调用start方法. 在新启的线程里运行的就是重写的run方法. 1 /** 2 * 集成Thread类 实现run() 3 */ 4 public class C1 extends Thread { 5 6 @Override 7 public void run() { 8 try

java并发编程系列一、多线程

1.什么是线程 线程是CPU独立运行和独立调度的基本单位: 2.什么是进程 进程是资源分配的基本单位: 3.线程的状态 新创建   线程被创建,但是没有调用start方法 可运行(RUNNABLE)  运行状态,由cpu决定是不是正在运行 被阻塞(BLOCKING)  阻塞,线程被阻塞于锁 等待/计时等待(WAITING) 等待某些条件成熟 被终止  线程执行完毕 线程的生命周期及五种基本状态: 4.线程的优先级 成员变量priority控制优先级,范围1-10之间,数字越高优先级越高,缺省为5

Java并发编程(三)概念介绍

在构建稳健的并发程序时,必须正确使用线程和锁.但是这终归只是一些机制.要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的(Shared)和可变的(Mutable)状态的访问. 对象的状态是指存储在状态变量(例如实例或静态域)中的数据. 对象的状态可能包括其他依赖对象的域.比如某个HashMap的状态不仅是HashMap对象本身,还存储在许多Map.Entry对象中. "共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周

Java并发编程系列-(8) JMM和底层实现原理

8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信,典型的共享内存通信方式就是通过共享对象进行通信. 在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信,在java中典型的消息传递方式就是wait()和notify(). 线程之间的同步

Java并发编程系列 concurrent包概览

从JDK 1.5开始,增加了java.util.concurrent包,concurrent包的引入大大简化了多线程程序的开发. 查看JDK的API可以发现,java.util.concurrent包分成了三个部分,分别是java.util.concurrent.java.util.concurrent.atomic和java.util.concurrent.lock. >>Atomic包 API中的说明是“A small toolkit of classes that support loc

Java并发编程系列之二十五:线程池

线程池简介 在之前介绍Executor框架的文章中对线程池有一个初步的认识,实际上线程池这种设计思想在Java中很普遍,比如JVM中常量池,以及Web开发使用到的数据库连接池.这些池本质上还是Java中的对象池,因为池中存放的都是Java对象.回到线程池,几乎所有需要异步或者执行并发任务的程序都可以使用到线程池.使用线程池带来的好处主要包括以下几个方面: 一,提高资源利用率.由于线程池中的线程使可以重复利用的,所以达到了循环利用的目的 二,提高响应速度.由于线程的创建也是需要开销的,如果请求到来

Java并发编程系列之十六:Lock锁

Lock锁简介 Lock锁机制是JDK 5之后新增的锁机制,不同于内置锁,Lock锁必须显式声明,并在合适的位置释放锁.Lock是一个接口,其由三个具体的实现:ReentrantLock.ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,即重入锁.读锁和写锁.增加Lock机制主要是因为内置锁存在一些功能上局限性.比如无法中断一个正在等待获取锁的线程,无法在等待一个锁的时候无限等待下去.内置锁必须在释放锁的代码块中释放

Java并发编程系列之二十八:CompletionService

CompletionService简介 CompletionService与ExecutorService类似都可以用来执行线程池的任务,ExecutorService继承了Executor接口,而CompletionService则是一个接口,那么为什么CompletionService不直接继承Executor接口呢?主要是Executor的特性决定的,Executor框架不能完全保证任务执行的异步性,那就是如果需要实现任务(task)的异步性,只要为每个task创建一个线程就实现了任务的异

Java并发编程的一些基本概念

1. 原子性 操作的原子性是即不能被分割的操作,和数据库中事务的原子性概念一致,即要么不执行,要么全部完成.有很多看起来具有原子性操作的实际上并不具有“原子性”,例如: “读取-修改-写入”操作:形式上像 i++ 一样简单的语句实际上并不是一个具有“原子性”的操作,它实际上包含三步: 读取原来的值 修改值 写入新的值 并且这三步之间是可以被分割执行的. 2. 竞态条件 在多线程环境下,遇到不恰当的执行时序会出现不正确结果的情况成为竞态条件,竞态条件并不一定会导致错误,还需要某种不恰当的执行时序.