java多线程创建方式以及线程安全
什么是线程
- 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。
线程的状态
新建(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多线程创建方式以及线程安全相关推荐
- Python3进阶--Socket编程、多线程(创建方式、线程通信、线程锁、线程池)
第一章 变量.常用循环体.代码结构.代码练习 第二章 列表.元组等数据结构.字符串驻留机制及字符串格式化操作 第三章 函数.面向对象.文件操作.深浅拷贝.模块.异常及捕获 第四章 项目打包.类和对象高 ...
- Java多线程创建方式初探
多线程概述 抢占式多任务 直接中断而不需要事先和被中断程序协商 协作多任务 被中断程序同意交出控制权之后才能执行中断 多线程和多进程区别? 本质的区别在于每个进程有它自己的变量的完备集,线程则共享相同 ...
- Java多线程:实现方式Thread与Runnable
转载自 Java多线程:实现方式 在Java中, 多线程的实现有两种方式: 扩展java.lang.Thread类 实现java.lang.Runnable接口 方法1 /** * @Descrip ...
- Java多线程详解(线程不安全案例)
嗨喽-小伙伴们我又来了, 通过前面两章的学习,我们了解了线程的基本概念和创建线程的四种方式. 附上链接: 1. Java多线程详解(基本概念) 2. Java多线程详解(如何创建线程) ...
- Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字
线程安全问题是多线程编程中最典型的一类问题之一.如果多线程环境下代码运行的结果是符合我们预期的,即该结果正是在单线程环境中应该出现的结果,则说这个程序是线程安全的. 通俗来说,线程不安全指的就是某一代 ...
- Java多线程-通讯方式
Java多线程-通讯方式 线程之间为什么要通信? 通信的目的是为了更好的协作,线程无论是交替式执行,还是接力式执行,都需要进行通信告知.那么java线程是如何通信的呢,大致有以下六种方式. Java线 ...
- Java 多线程(七) 线程间的通信
Java 多线程(七) 线程间的通信--wait及notify方法 线程间的相互作用 线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务. Object类中相关的方法有两个notify方法 ...
- Java 多线程(三) 线程的生命周期及优先级
Java 多线程(三) 线程的生命周期及优先级 线程的生命周期 线程的生命周期:一个线程从创建到消亡的过程. 如下图,表示线程生命周期中的各个状态: 线程的生命周期可以分为四个状态: 1.创建状态: ...
- Java多线程系列(五):线程池的实现原理、优点与风险、以及四种线程池实现
为什么需要线程池 我们有两种常见的创建线程的方法,一种是继承Thread类,一种是实现Runnable的接口,Thread类其实也是实现了Runnable接口.但是我们创建这两种线程在运行结束后都会被 ...
最新文章
- 如何优化 Java 性能?
- 移植uboot第七步:支持DM9000
- javascript中的链表结构—双向链表
- ast.literal_eval
- iconfont 图标宽高出问题_一个技巧,100,000,000+PPT图标就可以任性使用!【黑科技第11期】...
- Java 源码中 unchecked 什么意思
- android应用崩溃的调试方法
- webpack4.x版本与webpack-cli高版本之间存在的bug
- 最近很火的数据动图python_GitHub热榜第一,标星近万:这个用Python做交互式图形的项目火了...
- 潜艇大战java代码_java潜艇大战游戏源码项目
- Android零基础入门第14节:使用高速Genymotion,跨入火箭时代
- [经验分享] 【统计小百科】你知道AIC准则么?
- BSS/SSID/BSSID、VAP和ESS
- 解决VMware 虚拟机中的网络连接出现”受限制或无连接“问题的方法
- jqwidgets简单技术
- 腾讯招聘爬虫(Scrapy框架)
- 易语言程序假死优化_易语言假死无响应采用处理事件解决办法
- 葡萄汽水(Grape soda)
- 计算机怎么看显卡内存容量,显存容量是什么,详细教您查看显卡容量大小方法...
- BlockingQueue drainTo()
热门文章
- 服务器ftp连接成功不显示文件,ftp服务器不显示文件
- JdbcTemplate 数据访问工具
- 做网站:服务器,云服务器,云主机,虚拟主机有何区别?
- jquery之Uncaught Error: Syntax error, unrecognized expression: #
- Git中的所有配置文件
- snownlp的准确度也太低了吧[伤心][伤心]是不是只能弄购物评论数据集啊
- 机器学习:各种优化器Optimizer的总结与比较
- 电子水尺传感器的原理介绍
- 大模型时代,腾讯云“复制”腾讯|WAIC2023
- slurm节点,分区,作业信息说明