多线程

1.线程的引入

进程:
正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和资源。
线程:
是进程的单个顺序控制流,或者说就是一个单独执行的路径
一个进程如果只有一条执行路径,称之为单线程
一个进程如果有多条执行路径,称之为多线程
线程是包含在进程中。
举例:扫雷,360杀毒软件,百度网盘

了解三个关键词:
1、串行,指的是一个程序中所有的任务都是按照先后顺序执行的,在前一个任务还没有处理完的情况下,是不会进行处理下一个任务的
举例:理发店只有一个理发师,很多人去理发,先等前面的人理完发,再轮到后面的人。
2、并行,指的是将任务分给不同的处理器去处理,每一个处理器中再进行串行处理。
举例:火车站上有很多个窗口,多个窗口同时卖票,但是针对于一个窗口来说,一个人的一个人的去卖票
3、并发,实质上是一种现象,并发需要处理器的支持,比如在处理一个任务的时候操作系统可以进行调用再处理其他的任务,不论串行还是并行
都需要操作系统的支持并发。假设喝水是一个任务,每个火车站售票员,再售票的同时也能喝水,这就表示支持并发。比如:一个人在吃饭,吃着吃着电话来了,
他停止去吃饭然,去接电话,这就是并发。而这个人吗,没有停下吃饭而是一边打电话一边吃饭,这就叫做并行

JVM启动的时候是单线程还是多线程呢?
多线程:
main(主线程)
垃圾回收线程
所以在JVM启动的时候,最低要求要有两个线程存在,所以JVM启动的时候是多线程的。

2.创建线程的第一种方式:继承Thread类

    1、创建一个自定义类继承Thread类2、这个类要重写Thread类中的run方法为什么是run()方法呢?当线程启动之后,执行的代码逻辑仅是run()方法的代码逻辑3、根据这个类创建线程对象4、启动线程

面试题:调用start()与调用run()的区别
run()方法中仅仅是封装了被线程执行的代码,但是呢,直接调用run()与调用普通的方法方式没有任何区别
start()方法的调用,首先单独启动了一个线程,然后再由JVM去调用该线程类中的run()方法 // myThread1.start();

public class MyThreadDemo2 {public static void main(String[] args) {//每创建一个对象,相当于创建一个新的线程对象
//        MyThread1 myThread1 = new MyThread1();//启动线程
//        myThread1.run();
//        myThread1.run();//单纯的调用run方法仅仅表示的是一个对象调用普通的方法,所以这里依旧是单线程程序//要想看到多线程的效果,就必须换一种方式启动线程 start()//面试题:调用start()与调用run()的区别//run()方法中仅仅是封装了被线程执行的代码,但是呢,直接调用run()与调用普通的方法方式没有任何区别//start()方法的调用,首先单独启动了一个线程,然后再由JVM去调用该线程类中的run()方法//       myThread1.start();//当一个线程对象启动多次的时候,报错://IllegalThreadStateException 非法的线程状态异常
//        myThread1.start();//模拟多线程环境//至少创建2个及两个以上的线程对象MyThread2 myThread1 = new MyThread2();MyThread2 myThread2 = new MyThread2();myThread1.start();myThread2.start();/***      注意事项:*          1、启动线程调用的是start()方法*          2、线程的先后启动顺序,对结果没有影响*/}
}

MyThread2类:

package com.shujia.wyh.day02.day24;public class MyThread2 extends Thread{@Overridepublic void run() {for (int i=0;i<100;i++){System.out.println(getName()+":"+i);}}
}

3.如何给线程设置名字呢? 如何获取线程的名字呢?

通过构造方法给线程起名字:
Thread(String name) 分配一个新的 Thread对象。

如何获取线程的名字呢?
public final String getName()返回此线程的名称

 public class MythreadDemo3 {public static void main(String[] args) {//创建线程对象//通过构造方法给线程起名字//由于我们要模拟多线程环境,所以创建线程的个数为2个或2个以上/*  MyThraed3 thraed1 = new MyThraed3("斗罗大陆");MyThraed3 thraed2 = new MyThraed3("斗破苍穹");thraed1.start();thraed2.start();*///public final void setName(String name)将此线程//的名称更改为等于参数name 。MyThraed3 thraed1 = new MyThraed3();MyThraed3 thraed2= new MyThraed3();MyThraed3 thraed3 = new MyThraed3();thraed1.setName("一号厅");thraed2.setName("二号厅");thraed3.setName("三号厅");thraed1.start();thraed2.start();thraed3.start();//如何获取main方法所在的线程呢?//public static Thread currentThread()返回对当前正在执行//的线程对象的引用。System.out.println(Thread.currentThread().getName());//main}
}

MyThread3类:

public class MyThraed3 extends Thread {public MyThraed3() {}public MyThraed3(String name) {super(name);}@Overridepublic void run() {for (int i=0;i<=100;i++){System.out.println(getName()+":"+i);//getName()由于继承了Thread类所以可以通过此方法得到名字
}}
}

4.线程优先级priority

我们在前几个代码中都没有设置优先级,我们猜测一定会有一个默认的优先级。
默认的优先级是多少呢?5

获取线程优先级的方法:
public final int getPriority()返回此线程的优先级。
设置线程优先级的方法:
public final void setPriority(int newPriority)更改此线程的优先级。

public final static int MAX_PRIORITY = 10; 线程可以拥有的最大优先级。
public final static int MIN_PRIORITY = 1; 线程可以拥有的最小优先级。

总结:

1、线程的默认优先级是5
2、线程优先级的范围是1-10
3、线程优先级高仅仅表示的获取CPU时间片的机率会高一些,但是呢,并不是绝对会获取到

public class ThraedPriorityDemo4 {public static void main(String[] args) {MyThraed3 thraed1= new MyThraed3();MyThraed3 thraed2 = new MyThraed3();MyThraed3 thraed3 = new MyThraed3();thraed1.setName("学霸");thraed2.setName("学神");thraed3.setName("学渣");//获取t1,t2,t3线程的优先级System.out.println(thraed1.getPriority());//5System.out.println(thraed2.getPriority());//5System.out.println(thraed3.getPriority());//5//设置优先级//IllegalArgumentException 非法的参数传入
//        t1.setPriority(100);//newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITYthraed1.setPriority(3);thraed2.setPriority(6);thraed3.setPriority(10);thraed1.start();thraed2.start();thraed3.start();}
}

MyThread3类:

public class MyThraed3 extends Thread {public MyThraed3() {}public MyThraed3(String name) {super(name);}@Overridepublic void run() {for (int i=0;i<=10;i++){System.out.println(getName()+":"+i);//getName()由于继承了Thread类所以可以通过此方法得到名字
}}
}

结果:

5.加入休眠的方法

        //public static void sleep(long millis)//停1秒钟 1秒=1000毫秒
public class ThraedSleepDemo5 {public static void main(String[] args) {MyThraedSleep5 thraedSleep1 = new MyThraedSleep5();MyThraedSleep5 thraedSleep2 = new MyThraedSleep5();MyThraedSleep5 thraedSleep3 = new MyThraedSleep5();thraedSleep1.setName("曹操");thraedSleep2.setName("刘备");thraedSleep3.setName("孙权");thraedSleep1.start();thraedSleep2.start();thraedSleep3.start();}
}

MyThreadSleeep类:

public class MyThraedSleep5 extends Thread{@Overridepublic void run() {for (int i=1;i<=100;i++){System.out.println(getName()+":"+i);//加入休眠的方法//public static void sleep(long millis)//停1秒钟 1秒=1000毫秒try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}
}

6. 中断线程:

public final void stop():强行打断睡眠run()后面的代码不会执行
public void interrupt():打断睡眠,run方法后面的代码继续执行,执行完后,抛出异常

public class ThreadStopDemo9 {public static void main(String[] args) {MyStopThread myStopThread1 = new MyStopThread();myStopThread1.setName("雄安");myStopThread1.start();try {Thread.sleep(3000);// myStopThread1.stop();//强行打断睡眠run()后面的代码不会执行myStopThread1.interrupt();//打断睡眠,run方法后面的代码继续执行,执行完后,抛出异常} catch (InterruptedException e) {e.printStackTrace();}//  myStopThread1.stop();//强行打断睡眠run()后面的代码不会执行System.out.println("helloworld");}
}

MyStopThread类:

import java.util.Date;public class MyStopThread extends Thread{@Overridepublic void run() {System.out.println("开始的时间为:"+new Date());try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("结束的时间为:"+new Date());}
}

结果:

7.Join:一个线程调用该方法后,要到这个线程执行完毕后其他线程才能执行

public class ThreadJoinDemo6 {public static void main(String[] args) {MyJoinThread myJoinThread1 = new MyJoinThread();MyJoinThread myJoinThread2 = new MyJoinThread();MyJoinThread myJoinThread3 = new MyJoinThread();myJoinThread1.setName("赵云");myJoinThread2.setName("吕布");myJoinThread3.setName("杨戬");myJoinThread1.start();try {myJoinThread1.join(100);} catch (InterruptedException e) {e.printStackTrace();//赵云先执行完,下面线程才会执行}myJoinThread2.start();myJoinThread3.start();}
}

MyJoinThread类:

public class MyJoinThread extends Thread{@Overridepublic void run() {for (int i=0;i<=10;i++){System.out.println(getName()+":"+i);}}
}

结果:

8. 礼让线程:

public static void yield()
暂停当前正在执行的线程对象,并执行其他线程
它的作用是为了让多个线程之间运行的时候看起来更加和谐,但是呢,并不能保证多个线程一人一次。

public class ThreadYieldDemo7 {public static void main(String[] args) {/*礼让线程:public static void yield()暂停当前正在执行的线程对象,并执行其他线程它的作用是为了让多个线程之间运行的时候看起来更加和谐,但是呢,并不能保证多个线程一人一次。*/MyYiledThread7 myYiledThread1 = new MyYiledThread7();MyYiledThread7 myYiledThread2 = new MyYiledThread7();MyYiledThread7 myYiledThread3 = new MyYiledThread7();myYiledThread1.setName("唐僧");myYiledThread2.setName("孙悟空");myYiledThread3.setName("猪八戒");myYiledThread1.start();myYiledThread2.start();myYiledThread3.start();}
}
public class MyYiledThread7 extends Thread{@Overridepublic void run() {for (int i=0;i<=100;i++){System.out.println(getName()+":"+i);Thread.yield();//调用礼让方法}}
}

9. 后台线程:(守护线程)

public final void setDaemon(boolean on)

Java中有两类线程:用户线程,守护线程用户线程:在学习多线程之前,运行起来的一个个的线程都是用户线程守护线程:所谓守护线程,指的是程序在运行的时候,在后台提供了一个通
用的服务线程。比如说垃圾回收线程,他就是一个守护线程
并且这种线程并不是一定存在的,所以反过来说,只要程序存在守护线程,程序就不会终止。有用户线程不一定有守护线程,但有守护线程一定会有用户线程守护线程怎么去设置呢?public final void setDaemon(boolean on)注意:1、守护线程必须在启动之前进行设置2、当运行的程序只有一个线程的时候并且这个线程是守护线程的时候,Java虚拟机退出(程序停止)
public class ThreadDamonDemo8 {public static void main(String[] args) {MyDaemonThread8 myDaemonThread1 = new MyDaemonThread8();MyDaemonThread8 myDaemonThread2 = new MyDaemonThread8();MyDaemonThread8 myDaemonThread3 = new MyDaemonThread8();myDaemonThread1.setName("刘备");myDaemonThread2.setName("关羽");myDaemonThread2.setDaemon(true);//2设置了守护线程myDaemonThread3.setName("张飞");myDaemonThread3.setDaemon(true);myDaemonThread1.start();myDaemonThread2.start();myDaemonThread3.start();}
}

MyDaemonThread类:

public class MyDaemonThread8 extends Thread {@Overridepublic void run() {for (int i=0;i<100;i++){System.out.println(getName()+":"+i);}}}

10. 多线程的实现方案二:实现Runnable接口

    1、自定义一个类实现Runnable接口2、实现run()方法3、创建自定义类对象4、创建Thread线程对象,将自定义的对象作为参数传递到构造方法中
public class MyRunnableDemo10 {public static void main(String[] args) {MyRunnable10 myRunnable1 = new MyRunnable10();
//注意这里只要new一次MyRunnable10对象Thread thread1 = new Thread( myRunnable1);Thread thread2 = new Thread(myRunnable1);thread1.setName("刘备");thread2.setName("曹操");thread1.start();thread2.start();}
}

MyRunnable类:

public class MyRunnable10 implements Runnable{@Overridepublic void run() {for (int i=0;i<100 ;i++)
{//由于Runnable接口中没有getName()方法,所以这里无法使用获取线//程对象名字//间接调用,我们可以先获取当前线程的对象,然后再调用Thread//类中getName()方法System.out.println(Thread.currentThread().getName()+":"+i);
}    }
}

11 某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。

 两种方式实现继承Thread类

为了更加接近现实,我们加入延迟sleep
但是呢,加入延迟后,产生了两个问题:
1、相同的票我们卖了多次
CPU的操作是原子性导致的,CPU中小小时间片足矣运行多次
2、出现了第0张票和负数的票
线程的执行具有随机性和延迟性导致的,加入sleep后,线程变成阻塞状态,让其他线程执行。

public class SellTicketDemo12 {public static void main(String[] args) {TicketWindow12 window1 = new TicketWindow12();Thread thread1= new Thread(window1);Thread thread2= new Thread(window1);Thread thread3= new Thread(window1);thread1.setName("窗口一");thread2.setName("窗口二");thread3.setName("窗口三");thread1.start();thread2.start();thread3.start();}
}
public class TicketWindow12 implements Runnable{private int ticket=100;@Overridepublic void run() {while (true){if ( ticket>0){//窗口1,窗口2,窗口3//为了模拟更加真实的售票场景,我们加入延迟try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在卖出第:"+(ticket--)+"张票");//窗口1正在出售第100张票//窗口2正在出售第100张票//出现重复的原因是,CPU的操作是原子性,//由于tickets--是两步操作,先赋值输出再--//当窗口1赋值输出后,还没有来得及--,这时候窗口2也执行到了//这一步,此时tickets的值还没有发生变化//所以出现了相同的票卖了多次。//理想状态下://窗口1正在出售第100张票//窗口2正在出售第99张票//出现第0张票的现象解释:
//两个或3个窗口同时在tickets的值为1的时候,都进入到if之中,都会
//进行一次睡眠
//当第一个窗口睡眠结束,也打印结束,此时的tickets的值从1变成0//所以当后面的线程睡眠结束打印结果是0//负数的来源是当tickets的值为1的时候,三个线程都进入if语句可能//会造成的现象。}}}
}

结果:

上一个案例加入了延迟操作,出现的问题,其实就称之为:线程安全问题。
要想去解决这个问题,就要搞清楚哪些原因导致的问题出现:
(三点总结出是否会出现线程安全问题,缺一不可)
1、是否存在多线程环境
2、是否存在共享数据/共享变量
3、是否有多条语句操作着共享数据/共享变量

    回想一下上一个案例是否满足判读线程安全问题的条件:1、是否存在多线程环境 存在,有3个窗口线程2、是否存在共享数据/共享变量 存在,共享数据是100张票3、是否有多条语句操作着共享数据/共享变量 是三个条件都满足,由此可见,我们上一个案例出现问题是一个正常的现象,因为它同时满足以上3个条件如何解决这些问题呢?第1,2条件是我们无法改变的,我们只能想办法改变第3个条件,只要其中一个不满足,就不会发生线程安全问题。解决问题的思想:要是有一个办法可以将多条语句操作共享数据的代码给包成一个整体,在某个线程执行的时候,别的线程进不来就可以了。直到某个线程执行完一次run方法后,其他线程才能进入执行。Java提供了一个机制给我们使用,来解决线程安全的问题:同步安全机制解决方案一:同步代码块语句格式:格式:synchronized(对象){需要同步的代码;}1、这里的对象是什么呢?随便创建一个对象试试2、需要同步的代码又是哪些呢?多条语句操作共享数据的代码

对上面代码进行改进后:

 public static void main(String[] args) {TicketWindow13 window1 = new TicketWindow13();Thread thread1= new Thread(window1);Thread thread2= new Thread(window1);Thread thread3= new Thread(window1);thread1.setName("窗口一");thread2.setName("窗口二");thread3.setName("窗口三");thread1.start();thread2.start();thread3.start();}}
package com.shujia.wyh.day02.day24;public class TicketWindow13 implements Runnable {private int ticket=10;
private Object object=new Object();@Overridepublic void run() {while(true){synchronized (object){if (ticket>0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();System.out.println(Thread.currentThread().getName()+"正在卖出第:"+ticket--+"张票");}}}}
}

结果:

12.线程流程图


创建一个线程后该线程是处于就绪状态,并没有执行,当该线程抢到cpu执行权后就会执行,在此时如果有其他线程抢到cpu执行权,该线程就会回到就绪状态,再去抢cpu执行权,当该线程调用sleep()后,该线程就会进入阻塞状态,等到睡眠结束后就再回到就绪状态,当线程结束后就会死亡,就会变成垃圾。

【多线程】线程的引入,创建线程的方式,设置线程名字、获取名字,线程优先级priority,加入休眠的方法,,后台线程,礼让线程,Join,中断线程,某电影院,共有100张票线程流程图,3售票窗口,相关推荐

  1. 多线程实现4个窗口卖100张票

    技能点: 1.如何保证线程是多线程运行,而且不出现负票? 第一个while判断是外部判断,用于保持售票:而且在while循环中嵌入synchronized (){}是惯用方法,如果此处把while换为 ...

  2. 爬虫实战学习笔记_6 网络请求request模块:基本请求方式+设置请求头+获取cookies+模拟登陆+会话请求+验证请求+上传文件+超时异常

    1 requests requests是Python中实现HTTP请求的一种方式,requests是第三方模块,该模块在实现HTTP请求时要比urlib.urllib3模块简化很多,操作更加人性化. ...

  3. Java多线程:多线程同步安全问题的 “三“ 种处理方式 ||多线程 ”死锁“ 的避免 || 单例模式”懒汉式“的线程同步安全问题

    Java多线程:多线程同步安全问题的 "三" 种处理方式 ||多线程 "死锁" 的避免 || 单例模式"懒汉式"的线程同步安全问题 每博一文 ...

  4. pthread 立即停止线程_pthread线程的终止退出 | 线程的大量创建

    1. 线程只是从启动例程中返回,返回值是线程的退出码: 2. 线程调用了pthread_exit函数: 3. 线程可以被同一进程中的其他线程取消. ************************** ...

  5. Java模拟售票窗口代码_java多线程模拟售票,多个窗口售票

    package com.ma.thread001; /** * 多线程模拟售票,多个窗口售票 * @author ma * */ public class SellTicktDemo implemen ...

  6. 线程间协作的两种方式:wait、notify、notifyAll和Condition

    转载自  线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当 ...

  7. Java线程学习实例——采用同步锁,互斥锁与同步锁的区别,synchronized的使用方法

    栗子来源:https://blog.csdn.net/wenzhi20102321/article/details/52524545 首先对java中同步锁与互斥锁进行区分,主要来源于知乎中的大佬总结 ...

  8. C++多线程:thread类创建线程的多种方式

    文章目录 描述 函数成员简介 总结 描述 头文件 <thread> 声明方式:std::thread <obj> 简介 线程在构造关联的线程对象时立即开始执行,从提供给作为构造 ...

  9. 多线程—Thread类及线程三种创建方式及对比

    线程创建的3种方法: 1.继承Thread类并重写run方法 Thread类方法: Thread Thread.currentThread() :获得当前线程的引用.获得当前线程后对其进行操作. Th ...

最新文章

  1. Android架构纵横谈之二—基于性能的考虑(1)
  2. 程序员求职之道(《程序员面试笔试宝典》)之面试官箴言?
  3. abaqus的python安装文件在哪_python、abaqus执行脚本路径
  4. spring 扫描所有_SpringBoot和Spring到底有没有本质的不同?
  5. PHP输出毫秒时间戳
  6. 一部珍贵的130位作家的手稿集
  7. opencv获得图片的像素宽度_使用OpenCV实现摄像头测距
  8. Java Class 文件结构
  9. mockito模拟依赖注入_Mockito间谍–部分模拟
  10. 在C#中调用Java代码
  11. Matlab绘制图像后在指定点绘制坐标线以及标注变量
  12. 深信服SCSA认证复习笔记三
  13. HttpClient下载图片
  14. Win11快捷键大全
  15. 来电弹屏--线程间操作无效: 从不是创建控件的线程访问它
  16. 计算机的空间复用技术应用,MIMO技术的介绍
  17. LaTeX 数学公式大全
  18. 程序员加班看不上球赛崩溃,外卖小哥伸出援手:我帮你改代码
  19. 2021-2022年中国区块链发展形势展望.pdf.
  20. MACBOOK强制退出程序的方法

热门文章

  1. 如何在Ubuntu 18.04上安装Apache Kafka
  2. pytest、pytest.mark和pytest.fixture的用法
  3. 浏览器缓存导致的问题:
  4. 亚马逊云科技上的游戏服务:Lumberyard + Amazon GameLift + Twitch
  5. 磁盘管理——LVM详解
  6. 剑网三查服务器角色信息,《剑网3》怀旧服角色预创建今日开放 全民赠礼可交互腰挂展示...
  7. 优柔寡断的人必读忠告
  8. 红米3s微信无法连接到服务器,红米手机怎么更新不了微信8.0
  9. SourceTree初次使用commit一直崩溃解决方案
  10. 许嵩新歌《想象之中》首发 全新思想大碟将发行