多线程
进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
一个进程中至少有一个线程。
Java VM 启动的时候会有一个进程java.exe.
该进程中至少一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中。
该线程称之为主线程。
当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序
每个线程在栈区中都有自己的执行空间,自己的方法区、自己的变量。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程
window是多任务系统:cpu在不同任务间快速切换,
切换---任务---进程---线程 其实最终切换的是线程。
创建新执行线程有两种方法:
发现运行结果每一次都不同.
因为多个线程都获取cpu的执行权. cpu执行到谁,谁就运行.
明确一点:在某一个时刻,只能有一个程序在运行(多核除外).
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。
这就是多线程的一个特性:随机性.谁抢到谁执行,至于执行多长.cpu说的算
为什么要覆盖run方法呢?
Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码.该存储功能就是run方法.
即Thread类中的run方法,用于存储线程要运行的代码
主线程代码存在main中
新线程代码存在run 中
原来线程都有自己默认的名称。
Thread-编号 该编号从0开始。
getName(): 获取线程名称。
设置线程名称:setName或者构造函数
static Thread currentThread():获取当前线程对象。
窗口买票:
创建线程的第二种方式:实现Runable接口
为什么要有Runnable接口的出现?(重点)
1:通过继承Thread类的方式,可以完成多线程的建立
此方式有一个局限性,若该类已经有了自己的父类就不可以继承Thread类
可是该类还有部分代码需要被多个线程同时执行
只有对该类进行功能扩展,功能扩展就需要接口
java就提供了一个接口Runnable.其中定义了run方法
其实run方法的定义就是为了存储多线程要运行的代码
所以,通常创建线程都用第二种方式。
因为实现Runnable接口可以避免单继承的局限性。
2:将多线程要运行的代码的位置单独定义到接口中。
为其他类进行功能扩展提供了前提。
所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口
Runnable接口将线程要执行的任务封装成了对象
多线程的运行出现了安全问题。
问题的原因:
当多条语句在操作同一个线程共享数据时
一个线程对多条语句只执行了一部分,还没有执行完
另一个线程参与进来执行,导致共享数据的错误.
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。
在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式——同步代码块。
synchronized(对象) //任意对象都可以,这个对象就是一把锁,没啥影响
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行.
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁.
火车上的卫生间
同步的前提:
1.必须要有两个或者两个以上的线程。
2.必须是多个线程使用同一个锁。
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
同步函数:
需求:
银行有一个金库。
有两个储户分别存300员,每次存100,存3次。
函数为了是封装代码,锁synchronized也是为了封装代码
就是将同步关键字定义在函数上,让函数具备了同步性.
如何找问题:
1.明确哪些代码是多线程运行代码。
2.明确共享数据。
3.明确多线程运行代码中哪些语句是操作共享数据的。
明确1:run()方法中的,run中b.add(100);那么add方法中的代码也是
明确2:b和sum;
明确3:b.add(100)和sum=sum+n;System.out.println(…+sum)
共享数据sum有两条语句使用,可能有安全问题
线程0执行到sum=sum+n;cpu切到线程2,
线程2也执行sum=sum+n;sum就是200,再切换到线程1,
SOP(sum),线程1一下存了200,出现安全隐患
另一种方法把for(int x=0;x<3;x++){b.add(100)}也加锁
会出现线程0都存完,线程1才能进
同步函数用的是哪一个锁呢?
首先锁是一个对象 如Object obj = new Object()
函数需要被对象调用,那么函数都有一个所属对象引用.就是this.
所以同步函数使用的锁是this.
静态函数使用的锁是什么呢?(面试)
通过验证,发现不在是this.因为静态方法中也不可以定义this.
静态函数进内存时,内存中没有本类对象.
但是类进内存时有对象,类进内存要先封装成class对象,即该类对应的字节码文件对象.
就是该类的字节码文件加载进内存就已经被封装成了对象
这个对象就是该类的字节码文件对象
类名.class 该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class.
同步代码块和同步函数的区别?
同步代码块使用的锁可以是任意对象.
同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象.
在一个类中只有一个同步,可以使用同步函数.
如果有多同步,必须使用同步代码块,来确定不同的锁.
所以同步代码块相对灵活一些.
-------------------------------------------------------
★考点:写一个延迟加载的单例模式?写懒汉式;
当出现多线程访问时怎么解决?加同步,解决安全问题;
效率高不高怎样解决?通过双重判断的形式解决.
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){ //锁是谁?字节码文件对象;
if(s == null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
return s;
}
}
---------------------------------------------------------