java-多线程
多线程
概述
-
在一个线程中有多条执行路径
-
进程:正在执行的程序,他代表着应用程序的执行区域
-
线程:进程执行路径,就是进程中一个负责程序执行的控制单元。
线程总是属于某个进程,进程中的多个线程共享进程的内存
关于多线程的问题
-
jvm启动时多线程还是单线程的,为什么?
多线程的,他至少启动了两个线程(主线程和垃圾回收线程)垃圾回收机制这个线程不可能是在程序执行完毕后才启动的,否则的话,我们的程序很容易出现内存溢出
-
调用start方法和run方法区别?
调用start方法后,线程进入就绪状态,此时线程对象仅有执行资格,还没有执行权。当该线程对象抢到了执行权的时,方可调用run方法,当run方法执行完毕后,线程死亡,不能复生。
-
线程的随机性的导致原因?
在同一时刻,CPU只能执行一个程序,这个多线程的程序其实是CPU的高速切换造成的。
-
什么时候使用多线程?以及创建线程的目的?
多线程的引入是为了解决现实生活中的需求的,提高解决问题的效率。
当许多对象要对同一有限资源进行操作的时候,我们就要使用多线程
-
线程状态的内容和每一个状态的特点?
创建线程对象后,并对这个对象进行了一些初始化工作,当调用了start方法后,这个状态局有了执行资格,但是此时还未获得执行权,进入到了就绪状态。当抢到执行权后进入了运行状态,此时该线程既有了执行资格,又有了执行权。当调用run方法后,此线程进入了死亡状态。当然你也可以调用Stop方法令其强制死亡。在运行状态的时候,如果该线程调用了sleep或者wait等方法后,他会进入到阻塞状态,此时这个对象释放了执行资格和执行权,当它的sleep或者wait等方法后,亦或者调用了notify方法,该线程被唤醒,又进入了就绪状态,如此周而复始。
创建线程的方式
-
继承Thread类
- 定义一个类继承Thread类
- 重写Thread类中的run()方法,run()方法里边是多线程要运行的代码;
- 创建定义的那个类的对象
- 调用start()方法开启线程,执行run()方法里的内容。
package com.xtslife.thread; /** * @author 小涛 * @create 2019-05-28 18:55 */ public class FirstThread extends Thread{ @Override public void run() { for (int i= 0;i<=10;i++){ System.out.println("线程"+getName()+"正在运行:"+i); } } public static void main(String[] args) { FirstThread t1 = new FirstThread(); FirstThread t2 = new FirstThread(); t1.start(); t2.start(); } }
线程的声明周期:创建-----阻塞-----运行----死亡
-
实现Runnable接口
- 定义一个类实现Runnable接口
- 重写Runable接口中的run()方法,run()方法里是多线程要运行的代码
- 创建定义的那个类的对象,并将其作为参数放置于Thread类的对象里。线程的任务都封装在Runnable接口子类对象的run方法中,所以要在线程对象创建时就必须明确要运行的任务。
- 调用start()方法启动线程,执行run()方法里的内容
- 总结:两种方法的比较:实现Runnable接口,将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务的封装成了对象,避免了java中单继承的局限性。
多线程的安全问题
- 产生的原因
- 线程访问的延迟
- 线程的随机性
- 线程安全问题表现?原因?解决思想?解决具体的体现
- 当以个线程对象在执行run方法是的某一操作,其他线程进来了,并发的访问了临界资源,破坏了原子操作,造成了数据的不一致
- 多线程访问的延迟和线程的随机性产生了线程的安全问题
- 当某一线程对象进入run方法后,如果能做一个标记,说我已经在理面了,其他哥们(线程对象)你就等着吧,等我操作完了,出来了去掉标记你再进去吧,这样一来。原子操作就不会遭到破坏。
- 具体体现就是给那个原子操作枷锁,是整个操作同步,不让其他线程对象破坏,保证数据的一致性。
同步解决线程安全问题
-
同步代码块
- 同步代码块中的锁可以是任意对象,但是要在成员范围内定义
- 在局部的话,会导致锁发生变化,因为你每次执行方法,都会重新创建一个对象
- 同步的前提
- 至少要有两个线程
- 同一个锁
- 同步的好处:提高了安全性
- 同步的弊端:效率较低
-
同步函数
-
同步函数的使用
-
用synchronized关键字修饰方法即可
public synchronized void show(){ //需要的同步代码块 }
-
-
同步函数使用的锁
- 同步函数使用时this对象锁,静态同步函数的锁是(类名.class)
public class Singleton{ private Singleton(){} private static Singleton s = null; public static Singleton getInstance(){ if(s==null){ synchronized(Singleton.class){ if(s==null){ s= new Singleton(); } } } return s ; } }
-
死锁
每个线程都不会释放自己拥有的锁标记,却阻塞在另外的线程所拥有的锁标记的对象池中,就会造成死锁现象。
产生原因
假如有A何B两个锁,在A锁中要使用B锁,在B锁中要使用A锁,而他们都不想让,最终导致了死锁。
- 因为资源不足
- 进程运行推进的顺序不合适
- 资源分配不当
产生死锁的四个条件
-
互斥条件:一个资源每次只能被一个进程使用
-
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
-
不剥夺条件:进程已获得的资源,在未使用之前,不能强行剥夺。
-
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
只要上述条件之一不满足,就不会发生死锁。
如何解决?
- 不同时在A锁中使用B锁,B锁中使用A锁
线程状态
-
CPU
- CPU的执行资格:可以被CPU的处理,在处理队列中排队
- CPU的执行权:正在被CPU的处理
-
创建:使用start()开启线程
-
运行:具备着执行资格,具备着执行权。
-
冻结:释放执行权,同时释放执行资格
从运行到冻结的方式
- sleep(time),sleep(time)时间到,进入临时阻塞状态(具备着执行资格,但是不具备执行权,正在等待执行权)
- wait()线程等待,notify()线程唤醒,进入临时阻塞状态
-
消亡
从运行到消亡的方式
- stop()终止线程
- run()方法结束,线程任务结束
几个方法的使用
-
为什么wait(),notify(),notifyAll()都定义在Object类中?
- 这些方法存在于同步中
- 使用这些方法时必须要标识所属的同步锁
- 锁可以是任意对象,所以任意兑现调用的方法一定定义Object类中
-
wait()和sleep()的区别?
- 对时间指定货而言
- wait():可以不指定时间
- sleep():必须指定时间
- 对执行权和锁而言
- wait():释放了CPU的执行权(资格也没了),释放锁,存储于线程池。
- sleep():释放CPU执行权,不释放锁(会自动醒)
- 对时间指定货而言
-
停止线程
- 通过控制循环
- interrupt()方法
- stop已过时,被interrupt取代
-
守护线程,后台线程
你只要把一个线程设置为守护线程,那么主方法线程结束,不管什么情况,守护线程就结束。
-
join:加入线程,把执行权抢夺,自己执行完毕,其他线程才可能有机会执行
-
toString():线程名称,优先级,线程组(是有多个线程组成的,默认的线程组是main)
-
yield():让本线程暂停执行,把执行权给其他线程
-
setProproty(int num):设置线程的优先级
-
getPrinrity()获取线程的优先级
- 线程级别1-10
- 默认级别5
LOCK&Condition接口
Scanner类
接收从键盘输入的数据
- 这是java中提供的类。在util包中
- 基本用法:
- Scanner sc = new Scanner(System.in);
- int a =sc.nextInt();