线程相关概念

程序

程序就是为完成特定任务、用某种语言编写的一组指令的集合。简单来说就是我们写的代码。

进程

  1. 进程是指运行中的程序,比如我们打开一个应用,就是启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用另一个应用,又启动了一个进程,操作系统就会为该应用分配新的内存空间。(简单来讲就是运行几个程序)
  2. 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程。

什么是线程

  1. 线程有进程创建的,是进程的一个实体。
  2. 一个进程可以有多个线程。

其他相关概念

  1. 单线程:同一个时刻,只允许执行一个线程。
  2. 多线程:同一个时刻,可以执行多个线程,比如一个迅雷进程,可以同时下载多个文件。
  3. 并发:同一时刻,多个任务交替执行,造成一种“貌似同时”的感觉,简单来说单核cpu实现的多任务就是并发。
  4. 并行:同一时刻,多个任务同时执行。多核cpu可以实现并行。

线程的基本使用

创建线程的两种方式

  1. 继承Thread类,重写run方法。
  2. 实现Runnable接口,重写run方法。

继承Thread类

package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/24 18:15*/
public class Demo01 {public static void main(String[] args) throws InterruptedException {//创建Cat对象,可以当作线程使用Cat cat = new Cat();cat.start();//启动线程/*(1)public synchronized void start() {start0();}(2)start0();是本地方法,由jvm调用,底层用c/c++实现的真正实现多线程的效果的是start0(),而不是runprivate native void start0();*///当main(主)线程启动一个子线程(Thread-0)时,主线程不会阻塞,会继续执行。for (int i = 0; i < 10; i++) {System.out.println("主线程" + i);Thread.sleep(1000);}}
}//1.当一个类继承了Thread类,该类就可以当作线程使用
//2.程序员可以重写run方法,写上自己的业务代码
//3.run Thread类实现了Runnable接口的run方法
/*@Overridepublic void run() {if (target != null) {target.run();}}*/
class Cat extends Thread {int times = 0;@Overridepublic void run() {//该线程每隔1秒,在控制台打印"卷王上线,开始干活"while (true) {System.out.println("卷王上线,开始干活" + (times++) + "线程名:" + Thread.currentThread().getName());try {Thread.sleep(1000);//线程休眠,单位为毫秒} catch (InterruptedException e) {e.printStackTrace();}if (times == 10) {break;}}}
}

实现Runnable接口

  1. Java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然行不通。
  2. Java设计者们提供了另外一种方式创建线程,就是通过实现Runnable接口来创建线程。
package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/25 11:17* 通过实现Runnable接口创建进程*/
public class Demo02 {public static void main(String[] args) {
//        Door door = new Door();
//        //door.start();此时不能调用start方法
//        //可以创建Thread对象,把dog对象(即实现了Runnable接口的实现类)放入Thread
//        Thread thread = new Thread(door);
//        thread.start();Bee bee = new Bee();//此对象实现了Runnable接口Proxy proxy = new Proxy(bee);proxy.start();}
}
class Door implements Runnable{//通过实现Runnable接口,开发线程@Overridepublic void run() {int times = 0;while (true) {System.out.println("卷王上线,开始干活" + (++times) + "线程名:" + Thread.currentThread().getName());try {Thread.sleep(1000);//线程休眠,单位为毫秒} catch (InterruptedException e) {e.printStackTrace();}if (times == 10) {break;}}}
}
//线程代理类,模拟了一个简单的Thread类
class Proxy implements Runnable{private Runnable bee=null;//定义一个属性,类型是Runnable@Overridepublic void run() {if (bee!=null){bee.run();//动态绑定机制(运行类型时bee)}}public Proxy(Runnable bee){this.bee=bee;}public void start(){start0();//底层的这个star0方法才是真正实现多线程方法的,这里是模拟的}private void start0() {run();}
}
class Animal{}
class Bee extends Animal implements Runnable{int times=0;@Overridepublic void run() {while (true) {System.out.println("蜜蜂嗡嗡嗡");++times;try {Thread.sleep(1000);//线程休眠,单位为毫秒} catch (InterruptedException e) {e.printStackTrace();}if (times >= 10) {break;}}}
}

继承Thread vs 实现Runnable的区别

  1. 从Java设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档可以看到Thread类本身就实现了Runnable接口
  2. 实现Runnable接口方式更加适合多线程共享一个资源的情况,并且避免了单继承的限制。
package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/26 11:16*/
public class Demo04 {public static void main(String[] args) {
//        SellTicket1 sellTicket01 = new SellTicket1();
//        sellTicket01.start();
//        SellTicket1 sellTicket02 = new SellTicket1();
//        sellTicket02.start();
//        SellTicket1 sellTicket03 = new SellTicket1();
//        sellTicket03.start();//        出现票数超卖情况SellTicket2 sellTicket2 = new SellTicket2();new Thread(sellTicket2).start();new Thread(sellTicket2).start();new Thread(sellTicket2).start();}
}
class SellTicket1 extends Thread{private static int ticket=100;@Overridepublic void run() {while (true) {if (ticket<=0){System.out.println("全部卖完");break;}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数:"+(--ticket));}}
}
class SellTicket2 implements Runnable{int ticket=100;@Overridepublic void run() {while (true) {if (ticket<=0){System.out.println("全部卖完");break;}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数:"+(--ticket));}}
}

线程终止

  1. 当线程完成任务后,会自动退出。
  2. 还可以根据变量来控制run方法退出的方式来停止线程。
package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/26 14:21*/
public class Demo05 {public static void main(String[] args) throws InterruptedException {T t = new T();new Thread(t).start();for (int i = 1; i <=60; i++) {Thread.sleep(1000);System.out.println("main线程正在运行。。。");if (i==30){//设置定时终止线程t.setLoop(false);}}}
}
class T implements Runnable{private boolean loop=true;public boolean isLoop() {return loop;}public void setLoop(boolean loop) {this.loop = loop;}@Overridepublic void run() {while (loop) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在运行。。。。");}}
}

线程常用方法

方法名 方法说明
setName //设置线程名称,使之与参数name相同
getName //返回该线程的名称
start //使该线程开始执行;Java虚拟机底层调用start0方法
run //调用线程对象run方法
setPriority //更改线程的优先级
getPriority //获取线程优先级
sleep //在指定的毫秒数让正在执行的线程休眠(暂停执行)
interrupt //中断线程

注意事项

  1. start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新的线程。

  2. 线程优先级的范围。

  3. interrupt,中断线程,但是没有真正的结束线程。所以一般用于中断正在休眠线程。

  4. sleep:线程的静态方法,使当前线程休眠。

package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/27 10:13*/
public class Demo06 {public static void main(String[] args) throws InterruptedException {L l = new L();l.setName("猪八戒");l.setPriority(Thread.MIN_PRIORITY);l.start();//测试优先级System.out.println("默认优先级="+Thread.currentThread().getPriority());//测试interruptThread.sleep(3000);l.interrupt();}
}
class L extends Thread{@Overridepublic void run() {for (int i = 0; i <=100; i++) {System.out.println(Thread.currentThread().getName()+"吃包子。。。");}try {System.out.println(Thread.currentThread().getName()+"休眠中。。。");Thread.sleep(20000);} catch (InterruptedException e) {//当该线程执行了一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码System.out.println(Thread.currentThread().getName()+"被interrupt了");}}
}

常用方法二

  1. yield:线程的礼让。礼让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功。

    1. Thre ad.yield();
  2. join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务。

想插队的线程.join();

package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/27 10:56*/
public class Demo07 {public static void main(String[] args) throws InterruptedException {S s = new S();s.setName("子线程");s.start();for (int i = 1; i <= 20; i++) {System.out.println("main线程吃包子"+i);Thread.sleep(1000);if (i==5){System.out.println("让子线程先吃");//s.join(); //线程插队,先让s线程执行完毕Thread.yield();//礼让,不一定成功System.out.println("子线程吃完,main线程继续");}}}
}
class S extends Thread{@Overridepublic void run() {for (int i=1;i<=20;i++) {System.out.println(Thread.currentThread().getName()+"吃包子"+i);try {sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

用户线程和守护线程

  1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束。
  2. 守护线程:一般是为了工作线程服务的,当所有的用户线程结束,守护线程自动结束。
  3. 常见的守护线程:垃圾回收机制。
package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/27 14:21*/
public class Demo08 {public static void main(String[] args) throws InterruptedException {D d = new D();//将d设置为守护线程,当所有线程结束后,d也就自动结束//如果没有设置,那么即使main线程执行完毕,d也不退出。d.setDaemon(true);d.start();for (int i = 1; i <=100; i++) {Thread.sleep(50);System.out.println("hi"+i);}}
}
class D extends Thread{@Overridepublic void run() {for (;;){try {sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("hello");}}
}

线程的生命周期

JDK中Thread.State枚举表示线程几种状态

线程状态,线程状态可以处于以下几种状态之一:

  • NEW        至今尚未启动的线程处于这种状态。
  • RUNNABLE    正在Java虚拟机中执行的线程处于这种状态。
  • BLOCKED   受阻塞并等待某个监视器锁的线程处于这种状态。
  • WAIING     无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
  • TIMED_WAIING  等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
  • TERMINATED   已退出的线程处于这种状态。

在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反应所有操作系统线程状态。

线程状态转换图

package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/27 16:08*/
public class Demo09 {public static void main(String[] args) throws InterruptedException {F f = new F();System.out.println(f.getName()+"状态:"+f.getState());f.start();while (Thread.State.TERMINATED!=f.getState()){System.out.println(f.getName()+"状态:"+f.getState());Thread.sleep(500);}System.out.println(f.getName()+"状态:"+f.getState());}
}
class F extends Thread{@Overridepublic void run() {for (int i = 1; i <=10 ; i++) {System.out.println("hi"+i);try {sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

Synchronized

线程同步机制

  1. 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
  2. 也可以理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该地址内存地址进行操作。

同步具体方法

1.同步代码块

synchronized (对象){//得到对象的锁,才能操作同步代码

//需要被同步代码

}

2.synchronized还可以放到方法声明里,表示整个方法-为同步方法

pubilc synchronized void m(String name){

//需要被同步代码

}

3. 如何理解:就像你走进银行24小时自主取款机里,门会自动落锁,只有当你从里面出来之后才能有下一个人进去操作取款机。

package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/27 17:01*/
public class Demo10 {public static void main(String[] args) {//解决超卖问题SellTicket4 sellTicket2 = new SellTicket4();new Thread(sellTicket2).start();new Thread(sellTicket2).start();new Thread(sellTicket2).start();}
}
class  SellTicket4 implements Runnable{private int ticket=100;private boolean loop =true;public synchronized void sell(){if (ticket<=0){System.out.println("全部卖完");loop=false;return;}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数:"+(--ticket));}@Overridepublic void run() {while (loop) {sell();}}
}

互斥锁

基本介绍

  1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
  2. 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
  3. 关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任意时刻只能由一个线程访问。
  4. 同步的局限性:导致程序的执行效率要降低。
  5. 同步方法(非静态)的锁可以是this,也可以是其他对象(要求是同一对象)。
  6. 同步方法(静态)的锁为当前类本身。
package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/28 9:46* 互斥锁*/
public class Demo11 {public static void main(String[] args) {//解决超卖问题SellTicket5 sellTicket2 = new SellTicket5();new Thread(sellTicket2).start();new Thread(sellTicket2).start();new Thread(sellTicket2).start();//同步方法(非静态)的锁可以是this,也可以是其他对象(要求是同一对象)//下面是不同对象,所以不会同步。
//        SellTicket5 sellTicket3 = new SellTicket5();
//        SellTicket5 sellTicket4 = new SellTicket5();
//        SellTicket5 sellTicket5 = new SellTicket5();
//        new Thread(sellTicket3).start();
//        new Thread(sellTicket4).start();
//        new Thread(sellTicket5).start();}
}
class  SellTicket5 implements Runnable{private int ticket=100;private boolean loop =true;Object object=new Object();//1.public synchronized static void m1(){}锁是加在SellTicket5.class//2.如果在静态方法中,实现一个同步代码块。
//    synchronized (SellTicket5.class){
//        System.out.println("m2");
//    }public synchronized static void m1(){}public void m2(){synchronized (SellTicket5.class){if (ticket <= 0) {System.out.println("全部卖完");loop = false;return;}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余票数:" + (--ticket));}}//1.public synchronized void sell(){}就是一个同步方法//2.这时所在this对象//3.也可以在代码块上写synchronized,同步代码块,互斥锁还是在this对象public /*synchronized*/ void sell(){//同步方法,在同一时刻,只能有一个线程来执行sell方法synchronized (/*this*/object) {if (ticket <= 0) {System.out.println("全部卖完");loop = false;return;}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余票数:" + (--ticket));}}@Overridepublic void run() {while (loop) {//sell();m2();}}
}

注意事项

  1. 同步方法如果没有使用static修饰:默认锁对象为this
  2. 如果方法使用static修饰,默认锁对象:当前类.class
  3. 实现的具体步骤:
    1. 需要分析上锁的代码
    2. 选择同步代码块或同步方法
    3. 要求多个线程的锁对象为同一个

线程的死锁

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生。

package com.study.srv.demo16;/*** @author Wen先森* @version 1.0* @date 2022/4/28 11:28* 线程死锁*/
public class Demo12 {public static void main(String[] args) {//模拟线程死锁DeadLock d1 = new DeadLock(true);DeadLock d2 = new DeadLock(false);d1.setName("A线程");d1.start();d2.setName("B线程");d2.start();}
}
class DeadLock extends Thread{static Object o1=new Object();//保证多线程,共享一个对象,这里使用staticstatic Object o2=new Object();boolean flag;public DeadLock(boolean flag) {//构造器this.flag = flag;}@Overridepublic void run() {//业务分析//1.如果flag为T,线程A就会先得到/持有o1对象锁,然后尝试去获取o2对象锁//2.如果线程A得不到o2对象锁,就会Blocked//3.如果flag为F,线程B就会先得到/持有o2对象锁,然后尝试去获取o1对象锁//4.如果线程B得不到o1对象锁,就会Blockedif (flag){synchronized (o1){System.out.println(Thread.currentThread().getName()+"进入o1");synchronized (o2){System.out.println(Thread.currentThread().getName()+"进入o2");}}}else {synchronized (o2){System.out.println(Thread.currentThread().getName()+"进入o2");synchronized (o1){System.out.println(Thread.currentThread().getName()+"进入o1");}}}}
}

释放锁

可以释放锁

  1. 当前线程的同步方法、同步代码块执行结束。

    1. 案例:上厕所,完事出来。
  2. 当前线程在同步代码块、同步方法中遇到break、return。
    1. 案例:没有正常的完事,经理叫他修改bug,不得已出来。
  3. 当前线程在同步代码块、同步方法中出现了未处理的Error或Excepttion,导致异常结束。
    1. 案例:没有正常的完事,发现忘带纸,不得已出来。
  4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
    1. 案例:没有正常的完事,觉得需要酝酿一下,所以出来等会再进去。

不会释放锁

  1. 线程执行同步代码块同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁。

    1. 案例:上厕所太困,眯了一会。
  2. 该线程执行同步代码块,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。
    1. 应尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用

Java基础——线程基础相关推荐

  1. 关于java线程同步的笔记_线程同步(JAVA笔记-线程基础篇)

    在多线程应用程序中经常会遇到线程同步的问题.比如:两个线程A.线程B可能会 "同时" 执行同一段代码,或修改同一个变量.而很多时候我们是不希望这样的. 这时候,就需要用到线程同步. ...

  2. Java多线程 ——线程基础和锁锁锁

    Java多线程(一) 一.线程的定义 二.Synchronize线程同步 三.偏向锁.自旋锁.重量级锁 四.volatile关键字 4.1.普通变量运算的物理意义 4.2.有无解决的方案 4.3.vo ...

  3. Java中线程基础-线程间的共享-volatile

    对象锁 package com.xiangxue.ch1.syn;import com.xiangxue.tools.SleepTools;/****类说明:演示对象锁和类锁*/ public cla ...

  4. [Java基础]线程基础与实现多线程

    代码如下: package MyThreadPack;public class MyThread extends Thread {@Overridepublic void run() {for (in ...

  5. java多线程并发基础汇总一

    一.java并发线程基础 文章目录 一.java并发线程基础 1.1 什么是线程 1.2 线程的创建方式 1.3 Object类中的方法:线程通知与等待 1.4 Thread类中的方法 1.4.1 等 ...

  6. java 线程 操作系统线程_线程基础:线程(1)——操作系统和线程原理

    1.概述 我在写"系统间通信技术专栏"的时候,收到很多读者的反馈.其中有一部分读者希望我抽空写一写自己关于对Java线程的使用经验和总结.巧的是,这个月我所在的技术团队也有很多同事 ...

  7. java线程基础巩固---线程生命周期以及start方法源码剖析

    上篇中介绍了如何启动一个线程,通过调用start()方法才能创建并使用新线程,并且这个start()是非阻塞的,调用之后立马就返回的,实际上它是线程生命周期环节中的一种,所以这里阐述一下线程的一个完整 ...

  8. 百度java的线程技术_自我提升(基础技术篇)——java线程简介

    前言:虽然自己平时都在用多线程,也能完成基本的工作需求,但总觉得,还是对线程没有一个系统的概念,所以,查阅了一些资料,理解那些大神和官方的资料,写这么一篇关于线程的文章 本来想废话一番,讲讲自己的经历 ...

  9. java线程基础_Java多线程基础

    前言 在我们工作和学习的过程中,Java线程我们或多或少的都会用到,但是在使用的过程上并不是很顺利,会遇到各种各样的坑,这里我通过讲解Thread类中的核心方法,以求重点掌握以下关键技术点: 线程的启 ...

最新文章

  1. sys.check_constraints
  2. C++~回溯+贪心法解决01背包问题
  3. html页面展示Json样式
  4. 让Entity Framework支持MySql数据库
  5. FAILED: Error in metadata: MetaException(message:Got exception: java.net.ConnectException
  6. unbantu上python安装步骤_如何在Ubuntu中安装Python 3.6?
  7. 拉丁超立方抽样matlab代码_想要数据分析更快?超良心的笔记本/台式电脑配置推荐!...
  8. PowerDesigner--comment和name互相复制
  9. Linux FrameBuffer操作(二十七)
  10. 大数据技术原理与应用----大数据概述
  11. web服务器ngix基础
  12. matlab导入txt数据画图
  13. POJ-1637 混合图欧拉回路-网络流求解
  14. DSP28335软件实验研究--DA_AD模块功能详解
  15. 斐波纳契数列 python123
  16. 西南科技大学 Python程序设计 班长选举
  17. 互联网晚报| 8月18日|未婚已育女性办理生育津贴不需要结婚证;拼多多将上线跨境电商平台;小米汽车将采用宁德时代麒麟和比亚迪刀片...
  18. unite17-shanghai-JPLee-netease-pangu-FullChinese
  19. 看到自己的朋友圈,我和我的小伙伴都惊呆了
  20. Python dict_values取第一个值

热门文章

  1. wERP SWOT分析方法
  2. iOS面向切面编程-AOP
  3. App不显示桌面图标
  4. 爱真的需要勇气,勇气需要我么?
  5. 这些电脑软件你使用过吗?
  6. 2021春天梯赛个人总结和感悟
  7. 【Java自学】搬砖中年人代码自学之路Lesson 1
  8. 物联网(java版本)臻识摄像头+威视显示屏
  9. 如何在3dMax 中为模型赋予材质教程
  10. 定义一个Person类(name,age,sno)进行数据封装,age的年龄范围设置为0到130,使用getAge返回年龄。