线程:通俗的讲就是进程的儿子,进程中包含线程,且最少包含一个线程
比如说JVM启动的时候有进程java.exe---该进程中至少有一个线程负责java程序的运行
而且这个线程运行的代码存在于main方法----称为主线程
和异常一样,java已经提供了对线程的描述,我们只需要继承Thread或实现Runable接口就可以成为线程
创建线程的步骤:
继承Thread类或实现Runnable接口
实现好处是避免了单继承的局限性
当一个类是另一个类的子类,但是该类中还必须使用多线程,此时采用实现方式
复写run方法
Thread类定义了一个功能
用于存储线程要运行的代码,该存储功能就是run方法
Thread类中的run方法-----也就是线程的存储空间
Start启动线程
虚拟机定义主线程运行的代码存放在main方法中-----因为程序要从main开始
自定义线程的代码存放位置是覆盖父类的run方法
一个简单的线程例子:
多线程运行中存在的安全问题:
当多个线程操作共享数据时,容易发生多线程安全问题
当A线程还没操作完成后,B线程进入操作
因为是在同一个run方法中,容易发生安全问题
场景:单身模式的懒汉式
出现安全问题后,可通过同步来进行解决
同步包括同步代码块和同步函数
解决办法是:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行
同步的前提:
1、必须要有两个或两个以上的线程
2、必须是多个线程使用同一个锁
必须保证同步中只能有一个线程-------->保证线程的安全
同步的好处是解决了多线程中的安全问题
但是同时也消耗了资源,每个线程都需要判断锁
同步代码块需要创建锁,持有锁的线程才可以进入同步代码块或同步函数
锁可以是任意类的对象,这里引用Object类的对象
锁机制相当于:
多个线程(A,B,C)进入以后,假设A线程先抢到执行权
进入锁,此时A持有锁,并将锁的某个状态位改为0,若在A出去之前,A被冻结
失去执行资格,B,C也无法进入,因为侦测到状态位为0,即没有获得锁
同步函数:在函数名前面用synchronized修饰
同步函数的锁是this
函数需要被对象调用,那么函数都有一个所属对象引用,就是this
当同步函数被static修饰后,其锁就不再是this
因为静态函数是属于类的,this不可以出现在静态函数中
静态函数进入内存时,内存中没有本类对象,但是一定有该类字节码文件的对象
程序在某些时刻会发生死锁
当在同步中嵌套同步,但是锁不同,就会发生死锁。
Obj锁中包含this锁,This锁中包含Obj锁
0线程拿着Obj锁,要this锁,1线程拿着this锁,要Obj锁