赶紧收藏!王者级别的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多线程学习笔记
进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...
随机推荐
- axios封装接口
我们一般都是在做一个大型项目的时候,需要用到很多接口时,我们为了方便使用,就把接口封装起来. 先安装axios命令 :npm install axios --save 那么思路是什么呢? 首先在src ...
- 某C++神作,就100句话而已
假设p是指针,当delete p;时,后面一定要p=NULL将p指向空 cin cout cerr 都是iostream类型的对象.cout<<"hello world" ...
- maven pom.xml 报错
首先介绍背景,在eclipse中导入一个maven的项目,在我之前的电脑上导入好用,在自己的电脑上导入居然pom报错了Missing artifact junit:junit:jar:4.11,还会有 ...
- opencv 学习1 gui属性
1.图像 先定一个小目标学习两个个功能 1.学习怎样读入一副画像,以及如何保存一副图像 2.学习三个函数cv2.imread(), cv2.imshow() , cv2.imwrite() 1.1读入 ...
- java 基础知识(java web 方面的)
1.java面向对象的基本特征:封装性,多态性,继承性. 2.Java的泛型:iterator接口主要有hasnext()方法,next()方法,remove()方法:collection接口继承了i ...
- Java入门基础知识点总结(详细篇)
Java入门基础知识点总结(详细篇)~~~~~目录 1.1 图解 1.1.1 Java基础知识点 1.1.2 Java基础语法的相关内容 1.2 关键字 1.3 标识符 1.3.1 标识符概念 1.3 ...
- Maven项目关系
Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),其中最重要的就是POM文件,可以指定项目类型,项目关系等信息,maven项目之间有三种关系. 依赖 ...
- Vue + ElementUI 后台管理模板推荐
最近学习和项目都用到了Vue和ElementUI,自己不是专业前端,搞这些UI上的东西还是有些难度,这里推荐两个Vue + ElementUI后台管理模板 vue-element-admin vue- ...
- SpringBoot 之 @ControllerAdvice 拦截异常并统一处理
在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有@Requ ...
- FTP的PORT(主动模式)和PASV(被动模式)
最近做一个项目用到FTP和其它系统进行文件传输,结果在FTP网络连接的问题上花了很多时间,由于太久没搞多FTP,忘记了FTP不单单开放21端口,客户端采用不同连接模式对网络有不同.在此重温一下FTP的 ...