1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Java多线程(线程创建 线程安全问题解决 线程状态)

Java多线程(线程创建 线程安全问题解决 线程状态)

时间:2022-11-21 13:22:18

相关推荐

Java多线程(线程创建 线程安全问题解决 线程状态)

一.基础概念

1.并发和并行

2.进程与线程

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多 个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创 建、运行到消亡的过程。

线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程 中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

3.线程调度

分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为 抢占式调度

二.主线程

三.多线程原理

1.创建多线程程序的**第一种方式:**创建Thread类的子类

java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类实现步骤:1.创建一个Thread类的子类2.在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么?)3.创建Thread类的子类对象4.调用Thread类中的方法start方法,开启新的线程,执行run方法void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。结果是两个线程并发地运行;当前线程(main线程)和另一个线程(创建的新线程,执行其 run 方法)。多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。java程序属于抢占式调度,那个线程的优先级高,那个线程优先执行;同一个优先级,随机选择一个执行

例子:

MyThread

domo01Thread

多线程随机性打印结果:

多线程内存图解:

2.创建多线程程序第二种方式:实现Runnable接口(尽量多使用,线程任务与线程开启相解耦)

java.lang.Runnable

Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。

java.lang.Thread类的构造方法

Thread(Runnable target) 分配新的 Thread 对象。

Thread(Runnable target, String name) 分配新的 Thread 对象。

实现步骤:1.创建一个Runnable接口的实现类2.在实现类中重写Runnable接口的run方法,设置线程任务3.创建一个Runnable接口的实现类对象4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象5.调用Thread类中的start方法,开启新的线程执行run方法实现Runnable接口创建多线程程序的好处:1.避免了单继承的局限性一个类只能继承一个类(一个人只能有一个亲爹),类继承了Thread类就不能继承其他的类实现了Runnable接口,还可以继承其他的类,实现其他的接口2.增强了程序的扩展性,降低了程序的耦合性(解耦)实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)实现类中,重写了run方法:用来设置线程任务创建Thread类对象,调用start方法:用来开启新线程 。如下例子:

3.用匿名内部类方式创建线程

匿名内部类方式实现线程的创建:

匿名:没有名字内部类:写在其他类内部的类匿名内部类作用:简化代码把子类继承父类,重写父类的方法,创建子类对象合一步完成把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成匿名内部类的最终产物:子类/实现类对象,而这个类没有名字格式:new 父类/接口(){重复父类/接口中的方法};例:1.线程的父类是Thread// new MyThread().start();new Thread(){//重写run方法,设置线程任务@Overridepublic void run() {for (int i = 0; i <20 ; i++) {System.out.println(Thread.currentThread().getName()+"-->"+"黑马");}}}.start();2.线程的接口Runnable//Runnable r = new RunnableImpl();//多态// new Thread(r).start()Runnable r = new Runnable(){//重写run方法,设置线程任务@Overridepublic void run() {for (int i = 0; i <20 ; i++) {System.out.println(Thread.currentThread().getName()+"-->"+"程序员");}}};new Thread(r).start();3.线程的接口简化:new Thread( new Runnable(){//重写run方法,设置线程任务@Overridepublic void run() {for (int i = 0; i <20 ; i++) {System.out.println(Thread.currentThread().getName()+"-->"+"传智播客");}}}).start();

四.多线程安全问题

1.产生原因:

单线程不会产生线程安全问题,多线程不共享资源也不会产生线程安全问题,多线程共享资源会产生线程安全问题

分析原因:

2.解决线程安全问题

卖票案例出现了线程安全问题

问题:卖出了不存在的票和重复的票

解决线程安全问题的第一方案:使用同步代码块

格式:synchronized(锁对象){可能会出现线程安全问题的代码(访问了共享数据的代码)}注意:1.通过代码块中的锁对象,可以使用任意的对象2.但是必须保证多个线程使用的锁对象是同一个3.锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行

例子:

main方法

接口实现类

同步原理:

解决线程安全问题第二方案:使用同步方法

使用步骤:1.把访问了共享数据的代码抽取出来,放到一个方法中2.在方法上添加synchronized修饰符格式:定义方法的格式修饰符 synchronized 返回值类型 方法名(参数列表){可能会出现线程安全问题的代码(访问了共享数据的代码)}1./*定义一个同步方法同步方法也会把方法内部的代码锁住只让一个线程执行同步方法的锁对象是谁?就是实现类对象 new RunnableImpl()也是就是this*/public /*synchronized*/ void payTicket(){synchronized (this){//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;}}}2./*静态的同步方法锁对象是谁?不能是thisthis是创建对象之后产生的,静态方法优先于对象静态方法的锁对象是本类的class属性-->class文件对象(反射)*/public static /*synchronized*/ void payTicketStatic(){synchronized (RunnableImpl.class){//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;}}}

解决线程安全问题第三方案:使用Lock方法

java.util.concurrent.locks.Lock接口

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

Lock接口中的方法:

void lock()获取锁。

void unlock() 释放锁。

java.util.concurrent.locks.ReentrantLock implements Lock接口

使用步骤:1.在成员位置创建一个ReentrantLock对象2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁

五.线程状态

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。