进程:正在进行的应用程序(静态)
线程:一个进程中的控制单元,一条执行路径,程序实际执行的是线程。
一个CPU只支持一个线程执行,因此单CPU采用随机性原理(CPU快速切换,哪个线程获取CPU执行权,哪个线程执行)
main()就是一个线程,成为主线程,主线程优先,cpu先把时间片给主线程
注:一个进程中所有线程都在该进程的虚拟空间中,使用进程的全局变量和系统资源
什么时候考虑使用多线程?当需要某两个方法同时进行的时候
创建线程两种方式
一、继承Thread,重写run()方法
(1)创建线程类对象,调用start()方法(对象与对象间资源不共享)
二、实现Runnable接口。好处:避免了单继承的局限
(1)定义类实现Runnable接口
class Test implements Runable
(2)重写接口中run()方法,运行start执行的就是run()方法里的内容
(3)创建实现接口类的对象(将需执行的任务实例化)
Test t=new Test()
(4)通过Thread生成一个线程,传入需执行任务,调用start()方法
Thread t1=new Thread(t)// Thread t1=new Thread(t,"线程名")
Start方法与run方法之间的调用关系
调用start()会先创建一个新的线程,这个线程进行一些初始化工作,然后再由这个线程调用run()
常用方法:
为什么sleep方法只能try catch异常,不能throw?
因为Runnable接口使用的是try catch 实现接口的类就不能使用throw了
线程的优先级
线程的优先级分为10种从1 – 10数越小优先级越低数越大优先级越大,优先级高低影响时间片的分配,优先级越高分配时间片的概率越大,反之越小。高优先级只能说明这个程序优先执行
线程礼让(礼让)
(static void)Yield();满足某一个条件的时候让出当前时间片让其它线程执行(只让出一次)
线程的加入
(void)Join:先执行加入的线程
有线程t1 t2想让t1执行完毕之后再执行t2要把t1加入到t2当中,才能保证t1执行完毕之后t2才执行
守护线程
setDaemon(boolean on)
在程序当中其它的线程都执行完毕之后,守护线程不管是否已经执行完毕都强制结束
Gc就是一个守护线程:垃圾回收器
线程的生命周期
多线程安全问题(使用线程同步解决) 没有安全问题使用线程异步,效率高
当run()方法体内的代码操作到了成员变量(共享数据)时,就可能会出现多线程安全问题(线程不同步问题)
多线程安全问题出现的原因
例:总共有100张票,两个窗口卖票,一窗口卖出第20张票时,正要把票的数据打印出来,这时二窗口卖出了第21张票,这就会导致最后打印出的票是第21张票。(每个窗口就是一个线程)
解决办法,少使用成员变量多用局部变量
为了解决多线程访问同一资源而出现的安全问题,我们可以选择给会出现安全问题的地方加上互斥锁synchronized,当某个代码块被锁住,表明该对象在同一时刻只能由一个线程访问
1.synchronized(this) //this表示的是当前对象
{ 被同步的代码; }
2. synchronized void 方法名{ } //默认锁的是当前对象,不推荐使用 因为方法中可能有一些部分并不需要同步,同步的代码越多效率越低
当锁住一段代码的时候,锁的钥匙只有当前执行的代码有,被锁住的这一段代码执行完后,才会把钥匙给别人
卖票的解决方法:
锁的问题,如何确定究竟应该所在什么位置?
启动的两个线程操纵的都是银行类里的Money属性,所以当第一个线程执行完之后,第二个线程操纵的是money+完后的钱,
而这个时候如果没有锁的话,当一个线程执行完money+之后,另一个线程操纵的是money+后的钱,当2线程也执行money+操作后第一个线程输出,就会输出错误数据。这个for循环控制的是每个线程只能执行6次,所以锁加到循环里,是每循环一次,两个线程都可以操作一次,而如果加到循环外,就变成了一个线程循环完六次,另一个线程再进行
面试题
线程1执行时,另一个线程可以同时执行方法m2么?,当然可以,锁只是针对锁住的代码
当有读和写两个线程,写线程应该被同步
死锁问题
解决办法:加大锁的粒度
线程安全对单例模式的影响
对于静态方法的的锁对象是什么?
当前类的字节码对应的class类--------类.class;
对于饿汉没有影响,对于懒汉有影响,因为如果不加锁的话,当两个线程访问的时候,一开始都没有对象,所以都想要去创建一个对象,结果可能会导致两个线程创建了两个对象,出现线程安全问题。
线程之间的通信
多线程访问不同资源称为线程之间的通信
wait notify 是object的方法
线程交互,当两个线程操纵的是两段代码,而两段代码都操作了同一个资源,如果想要两段代码互不影响,就要给两段代码加上同一个锁,使得一段代码的锁未释放的时候,另一端代码不可能执行
notify的弊端:随机唤醒一个被挂起的线程改进 notifyAll()唤醒所有被挂起的线程
synchronized的缺陷,多线程进行只读操作的时候,也需要一个一个进行降低了效率由此引入lock
Lock替代了之前线程的synchronized代码块,以面向对象的方式去解决线程安全的问题,该对象使用起来更加的灵活(使用前要创建对象)
Lock()加锁
Unlock()解锁
Condition:替代之前Object对象中waitnotifynotifyAll三个方法,也是通过面向对象的方式去解决问题。
Await挂起
Signal唤醒
SignalAll唤醒所有
如何把不安全的集合转为线程安全的集合
Collections工具类中提供了一系列的把不安全的集合转换为安全集合的方法只需要调用其方法即可
一个读一个写,如果想要写完再读需要怎么解决,需要给读和写加一个同一个锁,这样写的锁没释放,读就不能解开锁进行读操作,这也就是生产者消费者的问题,只有先生产才可能消费
网友评论