赶紧收藏!王者级别的Java多线程技术笔记,我java小菜鸡愿奉你为地表最强!
Java多线程技术概述
介绍多线程之前要介绍线程,介绍线程则离不开进程。
首先 ,
进程 :是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元;
线程:就是进程中的一个独立控制单元,线程在控制着进程的执行。一个进程中至少有一个进程。
多线程:一个进程中不只有一个线程。
一、线程与进程
进程:通俗来解释就是一个程序,一个App,打开任务管理器可以看到当前电脑中正在运行的进程。
线程:一个进程中一般包含多个线程,打开任务管理器也可以看到当前电脑中正在运行的线程每个各自执行自己的任务来实现进程的运行,当一个进程中的最后一个线程结束时,整个进程就结束了。
线程的6种状态:
- NEW(未启动的线程)
- RUNNABLE(执行的线程)
- BLOCKED(被阻塞的线程)
- WAITING(无限期等待的线程)
- TIMED_WAITING(有限期等待的线程)
- TERMINATED(已结束的线程)


二、Java中线程创建的三种方式
1.Thread类: 通过创建一个类来继承Thread类,在这个类中重写run()方法,通过这个类创建的对象就是一个线程。
class MyThread extends Thread{
@Override
public void run() {
//执行的Java语句
}
}
public static void main(String[] args) {
MyThread t = new MyThread();
//线程启动
t.start();
}
2.Runnable接口:通过创建一个类来实现Runnable接口,在这个类中重写run()方法,通过这个类创建的对象是一个线程任务,我们将这个任务传给一个Thread对象即可执行这个线程任务。(推荐使用这种方式,传入一个Runnable任务即可执行线程,跟使用线程池有关)
class MyRunnable implements Runnable{
@Override
public void run() {
//执行的Java语句
}
}
public static void main(String[] args) {
MyRunnable r = new MyRunnable1();
Thread t = new Thread(r);
t.start();
}
3.Callable接口(很少用的方式):创建方式与Runnable相似。创建此类线程会产生一个返回值,如果主程序要获取这个返回值,则主程序会在Callable线程运行结束后再运行,不获取的话,则两个线程并行。
三、线程中一些常用的方法
Thread的常用方法
1.String getName() //获取该线程的名字
2.void setName(String name) //设置该线程的名字
3.void start() //线程开始
4.static Thread currentThread() //获取当前线程对象
5.static void sleep(long millis) //让当前线程休眠,进入阻塞状态,传入的参数单位为毫秒
6.void setDaemon(boolean on) //将线程设置为守护线程或者用户线程
7.void interrupt() //中断此线程
8.int getPriority() //返回此线程的优先级
9.void setPriority(int newPriority) //更改此线程的优先级
10.Thread(Runnable target) //(构造方法)传入一个Runnable任务
11.Thread(String name) //(构造方法)为线程取一个名字
1、interrupt方法和stop方法
线程在运行的过程中两种中断线程的方法,一个是stop()方法,一个是interrupt()方法。
Sun公司在JDK1.2版本的时候将stop()方法改为过时了,因为这种中断线程的方式是在外部强制的,这可能会导致在中断过程数据丢失,所以是不安全的。
使用interrupt()方法则是一种安全的方式,当在线程外部调用interrupt()方法时,JVM会给运行的线程传递一个异常对象,当线程接收到异常对象时,线程就会终止。
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
t.interrupt();
}
2、守护线程和用户线程的设置
void setDaemon(boolean on)
使用setDaemon()方法可以将一个线程设置为守护线程或者用户线程(参数为TRUE时,是守护线程,参数为FALSE时为用户线程),一个线程被创建时默认是用户线程。
当用户线程和守护线程一起运行,当用户线程结束时,则守护线程就结束。
四、线程安全
线程异步:即多条线程同时执行一个任务时,这种情况下往往是出现数据错乱的情况,例如两个人同时对一个银行账户进行取钱,账户中有10000元,两个人同时取走5000元,结果账户中还剩余5000元。所以这种线程异步的情况是非常不安全的。
线程同步:即多条线程同时执行一个任务时,,当一个线程开始执行任务线程后,为这个任务加锁,其他线程等待次线程执行完任务线程后再进行抢夺执行任务的时间片。
1、实现线程安全的方法(以售票窗口买票为例)
1.1、synchronized方法(显式锁)
同步代码块
当一个售票窗口在卖票时,其他窗口等待。
语法:
synchronized (加锁对象){
//Java语句
}
class Runnable implements Runnable{
private int count = 10;
private Object o = new Object();
public void run() {
while(true){
synchronized (o){
if(count>0){
System.out.println(Thread.currentThread().getName()+"买票中");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票:"+count);
}else{
break;
}
}
}
}
}
同步方法
当时用同步方法时,当前加锁的对象默认为当前对象this
class Runnable implements Runnable{
private int count = 10;
public void run() {
while(true){
boolean flag = sale();
if(!flag){
break;
}
}
}
public synchronized boolean sale(){
//判断票数是否大于0,是返回true,否返回false
if(count>0){
System.out.println(Thread.currentThread().getName()+"买票中");
try {
//此处休眠0.5秒
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票卖出,票数减一
count--;
System.out.println("余票:"+count);
return true;
}else{
return false;
}
}
}
1.2、Lock方法(隐式锁)
使用Lock方法需要创建Lock对象,并在需要加锁是手动加锁,在需要解锁时手动解锁
class Runnable implements Runnable{
private int count = 10;
private Lock l = new ReentrantLock();
public void run() {
while(true){
l.lock();
if(count>0){
System.out.println(Thread.currentThread().getName()+"买票中");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票:"+count);
l.unlock();
}else{
l.unlock();
break;
}
}
}
}
1.3、显式锁和隐式锁的区别
(1)Sync:Java中的关键字,是由JVM来维护的。是JVM层面的锁。
(2)Lock:是JDK5以后才出现的具体的类。使用lock是调用对应的API。是API层面的锁。
(3)在使用sync关键字的时候,我们使用者根本不用写其他的代码,然后程序就能够获取和释放锁了。那是因为当sync代码块执行完成之后,系统会自动的让程序释放占用的锁。Sync是由系统维护的,如果非逻辑问题的话话,是不会出现死锁的。
(4)在使用lock的时候,我们使用者需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。手动获取锁方法:lock.lock()。释放锁:unlock方法。需要配合tyr/finaly语句块来完成。
(5)Sync是不可中断的。除非抛出异常或者正常运行完成
(6)Lock可以中断的。
(7)Sync:非公平锁
(8)lock:两者都可以的。默认是非公平锁。ReentrantLock(boolean fair),true是公平锁,false是不公平锁
五、死锁
概述
A和B两人分别进入试衣间1和试衣间2,A想等B出来后去试衣间2,自己则在试衣间1中等待,B想等A出来后去试衣间1,自己则在试衣间2中等待,最终2个人都在等对方出来,但是对方都不出来,导致一直僵持。
public class DeadLockTest {
public static void main(String[] args) {
A a = new A();
B b = new B();
//子线程中需要和主线程中同样的A和B对象
R r = new R(a,b);
Thread t = new Thread(r);
t.start();
b.say(a);
}
}
class B{
public synchronized void say(A a){
System.out.println("BBBBB");
a.reply();
}
public synchronized void reply(){
System.out.println("bbbbb");
}
}
class A{
public synchronized void say(B b){
System.out.println("AAAAA");
b.reply();
}
public synchronized void reply(){
System.out.println("aaaaa");
}
}
class R implements Runnable{
private A a;
private B b;
public R(A a, B b) {
this.a = a;
this.b = b;
}
public void run() {
a.say(b);
}
}
六、生产者与消费者
当厨师在做菜时,服务员休息状态,当厨师做完菜后,厨师休息,服务员端菜出去,等服务员端空盘子回来后,服务员继续休息,叫厨师继续做菜。依次循环
public class Test {
public static void main(String[] args) {
Food f = new Food();
Cook c = new Cook(f);
Waiter w = new Waiter(f);
//厨师线程
new Thread(c).start();
//服务员线程
new Thread(w).start();
}
}
class Cook implements Runnable{
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for(int i = 0;i<10;i++){
f.cook(i);
try {
//此处休眠0.5秒
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Waiter implements Runnable{
private Food f;
private boolean flag = true;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for(int i = 0;i<10;i++){
f.get();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Food{
private String name;
private String tasty;
private boolean flag = true;
public Food() {
}
public Food(String name, String tasty) {
this.name = name;
this.tasty = tasty;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTasty() {
return tasty;
}
public void setTasty(String tasty) {
this.tasty = tasty;
}
//厨师做菜
public synchronized void cook(int i){
if(flag){
if(i % 2 == 0){
this.setName("番茄炒蛋");
this.setTasty("咸");
}else{
this.setName("糖醋排骨");
this.setTasty("酸甜");
}
System.out.println("厨师做菜:"+this.getName()+",味道:"+this.getTasty());
}
flag = false;
//唤醒其他线程
this.notifyAll();
try {
//厨师线程休眠
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//服务员端菜
public synchronized void get(){
if(!flag){
System.out.println("服务员出菜:"+this.getName()+",味道:"+this.getTasty());
}
flag = true;
//唤醒其他线程
this.notifyAll();
try {
//服务员线程休眠
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
七、线程池(了解)
概述
当程序中需要执行许多的内容很少的线程时,线程创建所花费的时间就会多于每次线程运行的所耗费的时间,就是导致程序的效率大大降低。使用线程池的方法就可以为程序节省下创建线程的时间。
线程池的种类
ExecutorService service = Executors.创建方法
void execute(Runnable command) //指挥线程池执行任务
1.缓存线程池
任务线程传入时自动分配线程,线程不够时自动创建新线程
Executors.newCachedThreadPool() //创建缓存线程池
2.定长线程池
指定线程池线程的个数,任务线程传入时自动分配线程,线程不够时剩余任务线程排队等待线程池中的线程执行完毕
Executors.newFixedThreadPool(int nThreads) //创建定长线程池,传入线程池中线程的个数
3.单线程线程池
线程池中只有一个线程,任务线程传入时自动分配线程,一个任务执行时剩余任务线程排队等待线程池中的线程执行完毕
Executors.newSingleThreadExecutor() //创建单线程线程池
4.周期定长线程池
指定线程池线程的个数,任务线程传入时自动分配线程,可以设定任务线程第一次执行的延时时间和之后每次执行的间隔时间
Executors.newScheduledThreadPool(int corePoolSize)
//创建周期定长线程池,传入线程池中线程的个数
service.schedule(Runnable command, long delay, TimeUnit unit)
//线程定时执行,传入任务、时长和时长单位
service.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
//周期性定时执行,传入任务,初次执行延时时长,周期间隔时长和时长单位
最后
欢迎关注公众号:前程有光,领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结!
赶紧收藏!王者级别的Java多线程技术笔记,我java小菜鸡愿奉你为地表最强!的更多相关文章
- Java多线程学习笔记——从Java JVM对多线程数据同步的一些理解
我们知道在多线程编程中,我们很大的一部分内容是为了解决线程间的资源同步问题和线程间共同协作解决问题.线程间的同步,通俗我们理解为僧多粥少,在粥有限情况下,我们怎么去防止大家有秩序的喝到粥,不至于 ...
- Java多线程技术学习笔记(二)
目录: 线程间的通信示例 等待唤醒机制 等待唤醒机制的优化 线程间通信经典问题:多生产者多消费者问题 多生产多消费问题的解决 JDK1.5之后的新加锁方式 多生产多消费问题的新解决办法 sleep和w ...
- Java多线程技术:实现多用户服务端Socket通信
目录 前言回顾 一.多用户服务器 二.使用线程池实现服务端多线程 1.单线程版本 2.多线程版本 三.多用户与服务端通信演示 四.多用户服务器完整代码 最后 前言回顾 在上一篇<Java多线程实 ...
- java多线程学习笔记——详细
一.线程类 1.新建状态(New):新创建了一个线程对象. 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...
- JAVA多线程学习笔记(1)
JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...
- Java多线程学习笔记(一)——多线程实现和安全问题
1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...
- Java多线程(四)java中的Sleep方法
点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...
- Java多线程专题2: JMM(Java内存模型)
合集目录 Java多线程专题2: JMM(Java内存模型) Java中Synchronized关键字的内存语义是什么? If two or more threads share an object, ...
- Java多线程学习笔记
进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...
随机推荐
- Redis系列文章-数据结构篇
Redis系列文章 前言: 工作原因,在学习mybatis知识后,2个月没有补充新的知识了,最近拿起书本开始学习.打算写下这个Redis系列的文章. 目录结构如下: Redis内置数据结构 Redis ...
- 浮动布局问题多,还是用inline-block吧
说说知识陈旧的问题. 目前我的前端开发知识积累最大的问题就是版本问题,也许我已经经历了很多,尝试了很多, 但是有些知识的版本已经过时了,而我还没有来得及更新它们.更悲剧的可能是有些部分我还没有意识到. ...
- QQ群web前端分析三——pageSpeed
使用pageSpeed插件,试试页面分析,看看有没有什么问题.等会上图 第一个问题,大部分人使用默认图片,但是这个图片的url确不一样,导致重复请求了若干次,这个...., 第二个问题,图片没有指定默 ...
- 第05组 Alpha冲刺 (2/6)
.th1 { font-family: 黑体; font-size: 25px; color: rgba(0, 0, 255, 1) } #ka { margin-top: 50px } .aaa11 ...
- Kafka 消费者及消费者分区策略
消费方式: consumer 采用 pull(拉)模式从 broker 中读取数据. push(推)模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的. 它的目标是尽可能以最 ...
- java的for循环中遇到异常抛出后继续循环执行
@Testpublic void loopTryCatchTest() throws Exception { Map<String, Object> a = new HashMap(); ...
- 如何删除一台OSD主机
在ceph的一台OSD主机出现故障的时候,数据可以通过副本的机制进行恢复,之后通过删除osd的操作也能够将故障osd从osd tree当中删除掉,但是故障的 osd 的主机仍然会留在集群当中,通过 c ...
- mysql case when语句的使用
case具有两种格式.简单case函数和case搜索函数. 简单函数 CASE [col_name] WHEN [value1] THEN [result1]-ELSE [default] END 搜 ...
- beef+metasploit
beef调用metasploit模块,直接xss吊打 先进入beef的文件夹 对config.yaml进行修改 将metasploit的false改为true 进入这个文件夹 修改配置文件 检查met ...
- 精尽 MyBatis 源码分析 - MyBatis 初始化(三)之 SQL 初始化(上)
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...