什么是线程

  • 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。

线程的状态

  • 新建(New):创建后尚未启动的线程处于这种状态
  • 运行(Runable):Runable包括了操作系统线程状态的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。
  • 等待(Wating):处于这种状态的线程不会被分配CPU执行时间。等待状态又分为无限期等待和有限期等待,处于无限期等待的线程需要被其他线程显示地唤醒,没有设置Timeout参数的Object.wait()、没有设置Timeout参数的Thread.join()方法都会使线程进入无限期等待状态;有限期等待状态无须等待被其他线程显示地唤醒,在一定时间之后它们会由系统自动唤醒,Thread.sleep()、设置了Timeout参数的Object.wait()、设置了Timeout参数的Thread.join()方法都会使线程进入有限期等待状态。
  • 阻塞(Blocked):线程被阻塞了,“阻塞状态”与”等待状态“的区别是:”阻塞状态“在等待着获取到一个排他锁,这个时间将在另外一个线程放弃这个锁的时候发生;而”等待状态“则是在等待一段时间或者唤醒动作的发生。在程序等待进入同步区域的时候,线程将进入这种状态。
  • 结束(Terminated):已终止线程的线程状态,线程已经结束执行。

多线程创建方法

继承Thread

/*** @Author GocChin* @Date 2021/5/11 11:56* @Blog: itdfq.com* @QQ: 909256107* @Descript:*/
class MyThread extends Thread{@Overridepublic void run() {System.out.println(currentThread().getName()+"运行了");}
}
class Test{public static void main(String[] args) {MyThread myThread = new MyThread();System.out.println(Thread.currentThread().getName()+":运行了");myThread.start();}
}

实现Runable接口创建多线程

/*** @Author GocChin* @Date 2021/5/11 12:37* @Blog: itdfq.com* @QQ: 909256107* @Descript: 实现Runable接口的方式创建多线程* 1.创建一个实现了Runable接口的类* 2.实现类去实现Runable中的抽象方法,run();* 3.创建实现类的对象* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象* 5.通过Thread类的对象调用start()*/
class MThread implements Runnable{@Overridepublic void run() {for (int i = 0; i<100;i++){if (i%2!=0){System.out.println(i);}}}
}
public class ThreadTest1 {public static void main(String[] args) {//3.创建实现类的对象MThread mThread = new MThread();//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象Thread thread = new Thread(mThread);thread.start();}
}

Thread和Runable创建多线程对比

开发中:优先使用Runable
1.实现的方式没有类的单继承的局限性。
2.实现的方式跟适合处理多个线程有共享数据的情况。
联系:Thread类中也实现了Runable,两种方式都需要重写run()。

实现Callable接口

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;/*** @Author GocChin* @Date 2021/5/11 13:03* @Blog: itdfq.com* @QQ: 909256107* @Descript:*/
class MCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum=0;for(int i=0;i<100;i++){sum+=i;}return sum;}
}
public class CallableTest {public static void main(String[] args) {//执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(new MCallable());new Thread(integerFutureTask).start();//接受线程运算后的结果Integer integer = null;try {integer = integerFutureTask.get();System.out.println(integer);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}

与Runable相比,Callable功能更强大

相比run()方法可以有返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,比如获取返回结果

使用线程池进行创建

线程池创建的好处

  • 提高响应速度(减少了创建新线程的时间)
  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  • 便于线程管理:
    • corePoolSize:核心线程池的大小
    • maximumPoolSize:最大线程数
    • keepAliveTime:线程没有任务时最多保持多长时间后悔中止
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @Author GocChin* @Date 2021/5/11 13:10* @Blog: itdfq.com* @QQ: 909256107* @Descript:*/
class Thread1 implements Runnable{@Overridepublic void run() {for (int i=1;i<30;i++){System.out.println(Thread.currentThread().getName() + ":" + i);}}
}
public class ThreadPool {public static void main(String[] args) {//创建线程池ExecutorService executorService= Executors.newFixedThreadPool(10);Thread1 threadPool = new Thread1();for (int i=0;i<5;i++){//为线程池分配任务executorService.submit(threadPool);}//关闭线程池executorService.shutdown();}
}

Thread中的常用方法

  • start():启动当前线程;调用当前线程的run();
  • run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中。
  • currentThread():静态方法,返回当前代码的线程。
  • getName():获取当前线程的名字。
  • setName():设置当前线程的名字。
  • yield():释放当前cpu的执行权,切换线程执行。
  • join():在线程a中调用线程b的join(),此时线程a会进入阻塞状态,知道线程b完全执行完毕,线程a 才结束阻塞状态。
  • stop():强制线程生命期结束。(过时了,不建议使用)
  • isAlive():判断线程是否还活着。
  • sleep(long millitime):让当前线程睡眠指定的事milltime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。

线程的优先级

线程的优先级等级
  • MAX_PRIORITY:10
  • MIN_PRIORITY:1
  • NORM_PRIORITY:5
涉及的方法
  • getPriority():返回线程的优先值
  • setPriority(int newPriority):改变线程的优先级
说明
  • 线程创建时继承父线程的优先级
  • 低优先级知识获得调度的概率低,并非一定是在高优先级线程之后才被调用

线程的同步

多线程卖票

基于实现Runable的方式实现多线程买票

package demo2;/*** @Author GocChin* @Date 2021/5/11 13:37* @Blog: itdfq.com* @QQ: 909256107* @Descript: 创建三个窗口买票,总票数为100张,使用Runable接口的方式*      存在线程安全问题,待解决*/
class Thread2 implements Runnable{private  int ticket=100;@Overridepublic void run() {while (true){if (ticket>0) {System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);ticket--;}else {break;}}}
}
public class Test1 {public static void main(String[] args) {Thread2 thread2 = new Thread2();Thread t1 = new Thread(thread2);Thread t2 = new Thread(thread2);Thread t3 = new Thread(thread2);t1.setName("窗口一");t2.setName("窗口二");t3.setName("窗口三");t1.start();t2.start();t3.start();}
}

实现结果,存在重复的票

如果在买票方法中加入sleep函数

 public void run() {while (true){if (ticket>0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);ticket--;}else {break;}}}

则运行结果可能会出现-1,表示也是不正常的

理想情况
极端情况

在java中,我们通过同步机制,来解决线程的安全问题。

同步代码块

synchronized(同步监视器){//需要被同步的代码
}

说明

  • 操作共享数据的代码就是需要被同步的代码。
  • 共享数据:多个线程共同操作的变量,比如本题中的ticket就是共享数据。
  • 同步监视器:俗称:锁。任何一个类的对象都可以充当锁。要求:多个线程必须要共用统一把锁
  • 同步的方式,解决了线程的安全问题—好处。但是操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。-----局限性
  • 使用Runable接口创建多线程的方式中,可以使用this关键字;在继承Thread类中创建多线程中,慎用this充当同步监视器,可以考虑使用当前类充当同步监视器。Class clazz = Windows.class 因此 类也是一个对象
  • 包裹操作共享数据的代码 不能多也不能少

修改之后的代码:

package demo2;/*** @Author GocChin* @Date 2021/5/11 13:37* @Blog: itdfq.com* @QQ: 909256107* @Descript: 创建三个窗口买票,总票数为100张,使用Runable接口的方式*      存在线程安全问题,待解决*/
class Thread2 implements Runnable{private  int ticket=100;Object object = new Object();@Overridepublic void run() {while (true){synchronized(object) { //括号中的内容可以直接使用当前对象this去充当if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);ticket--;} else {break;}}}}
}
public class Test1 {public static void main(String[] args) {Thread2 thread2 = new Thread2();Thread t1 = new Thread(thread2);Thread t2 = new Thread(thread2);Thread t3 = new Thread(thread2);t1.setName("窗口一");t2.setName("窗口二");t3.setName("窗口三");t1.start();t2.start();t3.start();}
}

结果

继承Thread的方式,去使用同步代码块,需要将声明的锁对象设为statci,否则创建的对象的同步监视器不唯一,就无法实现。

package demo2;/*** @Author GocChin* @Date 2021/5/11 14:45* @Blog: itdfq.com* @QQ: 909256107* @Descript:*/
class WindowsTest2 extends Thread{private static int ticket=100;private static   Object obj = new Object();@Overridepublic void run() {while (true){synchronized (obj){ //这里不能使用this去充当,可以直接写一个Test.class   类也是对象if (ticket>0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(getName()+":买票,票号为:"+ticket);ticket--;}else {break;}}}}
}
public class  Test2{public static void main(String[] args) {WindowsTest2 w1 = new WindowsTest2();WindowsTest2 w2 = new WindowsTest2();WindowsTest2 w3 = new WindowsTest2();w1.setName("窗口一");w2.setName("窗口二");w3.setName("窗口三");w1.start();w2.start();w3.start();}
}

同步方法

如果操作共享数据的代码完整的声明在一个方法中,可以将此方法声明为同步的。


通过实现Runable的方式实现同步方法。

package demo2;/*** @Author GocChin* @Date 2021/5/11 13:37* @Blog: itdfq.com* @QQ: 909256107* @Descript: 创建三个窗口买票,总票数为100张,使用Runable接口的方式* 存在线程安全问题,待解决*/
class Thread3 implements Runnable {private int ticket = 100;@Overridepublic void run() {while (true) {show();}}private synchronized void show(){if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);ticket--;}}
}public class Test3 {public static void main(String[] args) {Thread3 thread3 = new Thread3();Thread t1 = new Thread(thread3);Thread t2 = new Thread(thread3);Thread t3 = new Thread(thread3);t1.setName("窗口一");t2.setName("窗口二");t3.setName("窗口三");t1.start();t2.start();t3.start();}
}

通过实现继承Thread的方式实现同步方法。使用的同步监视器是this,则不唯一,就会报错。所以将该方法定义为static。当前的同步换时期就变成Test4.class

package demo2;/*** @Author GocChin* @Date 2021/5/11 14:45* @Blog: itdfq.com* @QQ: 909256107* @Descript:*/
class WindowsTest4 extends Thread{private static int ticket=100;private static   Object obj = new Object();@Overridepublic void run() {while (true){show();}}public static synchronized void show(){//同步监视器不是this了,而是当前的类
//    public synchronized void show(){//同步监视器是this  ,t1,t2,t3if (ticket>0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":买票,票号为:"+ticket);ticket--;}}
}
public class  Test4{public static void main(String[] args) {WindowsTest4 w1 = new WindowsTest4();WindowsTest4 w2 = new WindowsTest4();WindowsTest4 w3 = new WindowsTest4();w1.setName("窗口一");w2.setName("窗口二");w3.setName("窗口三");w1.start();w2.start();w3.start();}
}

总结

  • 同步方法仍然设计到同步监视器,只是不需要我们去显示的声明。
  • 非静态的同步方法,同步监视器是:this静态的同步方法中,同步监视器是类本身。

Lock锁解决线程安全问题


synchronize与lock的异同

相同

  • 都可以解决线程安全问题

不同

  • synchronize机制在执行相应的同步代码以后,自动的释放同步监视器;Lock需要手动的启动同步lock(),同时结束同步也需要手动的实现unlock()。

建议优先使用顺序
Lock------>同步代码块(已经进入了方法体,分配了相应资源)---->同步方法(在方法体之外)

package demo2;import java.util.concurrent.locks.ReentrantLock;/*** @Author GocChin* @Date 2021/5/11 15:58* @Blog: itdfq.com* @QQ: 909256107* @Descript:*/
class Lock1 implements Runnable{private int ticket=50;//1.实例化private ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while(true){try {//2.调用lock锁定方法lock.lock();if (ticket>0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket);ticket--;}else{break;}} finally {//3.调用解锁方法lock.unlock();}}}
}
public class LockTest1 {public static void main(String[] args) {Lock1 lock1 = new Lock1();Thread t1 = new Thread(lock1);Thread t2 = new Thread(lock1);Thread t3 = new Thread(lock1);t1.setName("窗口一");t2.setName("窗口二");t3.setName("窗口三");t1.start();t2.start();t3.start();}
}

java多线程创建方式以及线程安全相关推荐

  1. Python3进阶--Socket编程、多线程(创建方式、线程通信、线程锁、线程池)

    第一章 变量.常用循环体.代码结构.代码练习 第二章 列表.元组等数据结构.字符串驻留机制及字符串格式化操作 第三章 函数.面向对象.文件操作.深浅拷贝.模块.异常及捕获 第四章 项目打包.类和对象高 ...

  2. Java多线程创建方式初探

    多线程概述 抢占式多任务 直接中断而不需要事先和被中断程序协商 协作多任务 被中断程序同意交出控制权之后才能执行中断 多线程和多进程区别? 本质的区别在于每个进程有它自己的变量的完备集,线程则共享相同 ...

  3. Java多线程:实现方式Thread与Runnable

    转载自  Java多线程:实现方式 在Java中, 多线程的实现有两种方式: 扩展java.lang.Thread类 实现java.lang.Runnable接口 方法1 /** * @Descrip ...

  4. Java多线程详解(线程不安全案例)

    嗨喽-小伙伴们我又来了, 通过前面两章的学习,我们了解了线程的基本概念和创建线程的四种方式. 附上链接: 1.  Java多线程详解(基本概念)​​​​​​​ 2. Java多线程详解(如何创建线程) ...

  5. Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字

    线程安全问题是多线程编程中最典型的一类问题之一.如果多线程环境下代码运行的结果是符合我们预期的,即该结果正是在单线程环境中应该出现的结果,则说这个程序是线程安全的. 通俗来说,线程不安全指的就是某一代 ...

  6. Java多线程-通讯方式

    Java多线程-通讯方式 线程之间为什么要通信? 通信的目的是为了更好的协作,线程无论是交替式执行,还是接力式执行,都需要进行通信告知.那么java线程是如何通信的呢,大致有以下六种方式. Java线 ...

  7. Java 多线程(七) 线程间的通信

    Java 多线程(七) 线程间的通信--wait及notify方法 线程间的相互作用 线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务. Object类中相关的方法有两个notify方法 ...

  8. Java 多线程(三) 线程的生命周期及优先级

    Java 多线程(三) 线程的生命周期及优先级 线程的生命周期 线程的生命周期:一个线程从创建到消亡的过程. 如下图,表示线程生命周期中的各个状态: 线程的生命周期可以分为四个状态: 1.创建状态: ...

  9. Java多线程系列(五):线程池的实现原理、优点与风险、以及四种线程池实现

    为什么需要线程池 我们有两种常见的创建线程的方法,一种是继承Thread类,一种是实现Runnable的接口,Thread类其实也是实现了Runnable接口.但是我们创建这两种线程在运行结束后都会被 ...

最新文章

  1. 如何优化 Java 性能?
  2. 移植uboot第七步:支持DM9000
  3. javascript中的链表结构—双向链表
  4. ast.literal_eval
  5. iconfont 图标宽高出问题_一个技巧,100,000,000+PPT图标就可以任性使用!【黑科技第11期】...
  6. Java 源码中 unchecked 什么意思
  7. android应用崩溃的调试方法
  8. webpack4.x版本与webpack-cli高版本之间存在的bug
  9. 最近很火的数据动图python_GitHub热榜第一,标星近万:这个用Python做交互式图形的项目火了...
  10. 潜艇大战java代码_java潜艇大战游戏源码项目
  11. Android零基础入门第14节:使用高速Genymotion,跨入火箭时代
  12. [经验分享] 【统计小百科】你知道AIC准则么?
  13. BSS/SSID/BSSID、VAP和ESS
  14. 解决VMware 虚拟机中的网络连接出现”受限制或无连接“问题的方法
  15. jqwidgets简单技术
  16. 腾讯招聘爬虫(Scrapy框架)
  17. 易语言程序假死优化_易语言假死无响应采用处理事件解决办法
  18. 葡萄汽水(Grape soda)
  19. 计算机怎么看显卡内存容量,显存容量是什么,详细教您查看显卡容量大小方法...
  20. BlockingQueue drainTo()

热门文章

  1. 服务器ftp连接成功不显示文件,ftp服务器不显示文件
  2. JdbcTemplate 数据访问工具
  3. 做网站:服务器,云服务器,云主机,虚拟主机有何区别?
  4. jquery之Uncaught Error: Syntax error, unrecognized expression: #
  5. Git中的所有配置文件
  6. snownlp的准确度也太低了吧[伤心][伤心]是不是只能弄购物评论数据集啊
  7. 机器学习:各种优化器Optimizer的总结与比较
  8. 电子水尺传感器的原理介绍
  9. 大模型时代,腾讯云“复制”腾讯|WAIC2023
  10. slurm节点,分区,作业信息说明