------- android培训、java培训、期待与您交流! ----------
进程:是一个正在执行的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立控制单元,线程在控制着进程的执行,一个进程中至少有一个线程。
Java VM启动的时候会有一个进程java.exe;
jvm启动不止一个线程,还有负责垃圾回收机制的线程
如何自定义一个线程:
1,Java已经提供的对线程描述就是Thread类,创建线程的一种方法就是继承Thread类;该类定义了一个功能,用于存储线程要执行的代码,该存储功能就是run方法;classDemo extends Thread{//1,定义类继承Thread
public voidrun(){//2,复写Thread类中的run方法,目的:将自定义的代码存储在run方法中,让线程运行;
执行语句;
}
}//3,在主函数中用 Demo类实例对象调用start方法,该方法的作用,启动线程、调用run方法;
如果在main方法中利用Demo的实例对象(创建线程)调用run方法,仅//仅是对象调用方法,而线程创建了,并没有运行;
为什么要覆盖run方法:Thread类中run方法是存储要执行代码,这些代码会根据实现的功能不同,存储指定的代码进去;
2,创建线程的另一种方法是声明实现Runnable接口类,该类然后实现run方法然后可以分配该类的实例,在创建Thread时作为一个参数来传递并启动:
classPrimeRun implements Runnable{
publicvoid run(){
执行语句;
}
}
创建并启动一个线程:
PrimeRunp = new PrimeRun();
newThread(p).start();
步骤:1,定义类实现Runnable接口;
2,覆盖Runnable接口中的run方法:将线程中运行的代码存放在run方法中;
3,通过Thread类创建线程对象;
4,将Runnable接口的子类对象作为实参传递给Thread类的构造函数,
为什么要将Runnable接口的子类对象传给Thread的构造函数:
因为自定义的run方法所属的对象时Runnable接口的子类对象, 所以要让线程去执行指定对象的run方法,就必须明确该run方法所属对象;
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法;
实现方式(Runnable)和继承方式(Thread)有什么区别?
实现方式好处:避免了单继承的局限性;在定义线程时,建议使用实现方式;
继承Thread类:线程代码存放Thread子类run方法中;
实现Runnable:线程代码存放在接口的子类run方法中;
使用多线程要小心安全问题,问题原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误;
解决方法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行;
Java对于多线程的安全问题提供了专业的解决方法:就是同步代码块;
synchronized(对象){//这个对象可以是任意类的对象,这个对象就叫做锁(监视器)
需要被同步的代码
}//持有锁的线程可以在同步块中执行,没有持有锁的线程即使获取了CPU的执行权,也进不去
同步前提:1,必须要有两个或者两个以上的线程;
2,必须是多个线程使用同一锁;
弊端:多个线程需要判断锁时,较为消耗资源,影响运行效率
如何找问题:
1,明确哪些代码是多线程运行代码;
2,明确共享数据;
3,明确多线程运行代码中哪些语句是操作共享数据;
同步函数:synchronied返回值类型 函数名(){}
函数需要被对象调用,那么函数都有一个所属对象应用,就是this,所以同步函数使用的锁就是this;
静态同步函数,使用的锁是该方法所在类的字节码文件对象,类名.class;
死锁:同步中嵌套同步,但锁不是同一个锁;
发现运行结果每一次都不同,因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行;明确一点,在某一个时候,只能有一个程序在运行(多核除外)
CPU在做着快速的切换,以达到看上去是同时运行的效果;这就是多线程的一个特性,随机性,谁抢到谁执行,至于执行多长,CPU决定。
线程状态:睡眠(sleep(time)),等待(wait()需要唤醒notify()),消亡(stop()),运行(start())特例:写个延迟加载的单例设计模式示例:临时状态:阻塞状态,具备运行资格、等待CPU的执行权;
/*
*懒汉式单例:特点,实例的延迟加载;
*懒汉式延迟加载的问题:当多个线程访问时会存在安全问题;
*安全问题解决:加同步的方式解决 ,可以使同步代码快,或使同步函数都行,但是降低程序效率,用双层判断可以解决效率问题;
*加同步使用的锁:该方法所在类的字节码文件对象
*/
class Single{
privatestatic Single s = null;
publicstatic Single getInstance(){
if(s== null)//双层判断
synchronized(Single.class){//加同步的方式解决安全问题//锁Single.class是:该方法所在类的字节码文件对象
if(s==null)//双层判断,来解决效率问题
s= new Single();//实例的延迟加载
}
}
returns;
}
}
练习1:创建两个线程和主线程交替运行;
线程都有自己默认的名称:Thread-编号,该编号从零开始;
static Thread currentThread();获取当前线程对象
getName():获取线程名称;
setName():设置线程名称(或者构造函数)
class Test extends Thread{
publicvoid run(){
for(inti=0;i<60;i++){
System.out.println("testrun:"+i);
}
}
}
class ThreadTest{
publicstatic void main(String[] args){
Testt1 = new Test();
Testt2 = new Test();
t1.start();//使用start方法是输出结果是不确定
t2.start();
//t1.run();//使用run方法输出的结果是确定的
//t2.run();
for(inti=0;i<60;i++){
System.out.println("main:"+i);
}
}
}
练习2:一个死锁程序
class MyLock{
staticObject locka = new Object();
staticObject lockb = new Object();
}
class Test implements Runnable{
privateboolean flag;
Test(booleanflag){
this.flag= flag;
}
publicvoid run(){
if(flag)
{
synchronized(MyLock.locka){
System.out.println("iflocka");
synchronized(MyLock.lockb){
System.out.println("iflockb");
}
}
}else
{
synchronized(MyLock.lockb){
System.out.println("elselockb");
synchronized(MyLock.locka){
System.out.println("elselocka");
}
}
}
}
}
class DeadLockTest{
publicstatic void main(String[] args){
newThread(new Test(true)).start();
newThread(new Test(false)).start();
}
}