自定义线程
进程:正在执行的程序称作为一个进程。进程负责了内存空间的划分。
线程:线程在一个进程中负责了代码的执行,,就是进程中一个执行单位。
多线程:在一个进程中有多个线程同时在执行不同的任务。
任何一个Java程序,jvm在运行的时候都会创建一个main线程执行main方法中的所有代码。一个Java应用程序至少有两个线程,一个是主线程main方法代码的执行,一个是垃圾回收器线程,负责了回收垃圾。
多线程的好处:1.解决了一个进程能同时执行多个任务的问题;2.提高了资源的利用率
多线程的弊端:1、增加CPU的负担;2、降低了一个进程中线程的执行概率;3、引发了线程安全问题;4、出现了死锁现象;
如何创建多线程:
方式一:1.自定义一个类继承Thread类;2该子类应重写Thread的run方法(自定义线程的任务代码就写在run方法中,自定义线程负责了run方法);3、创建Thread的子类对象,并且调用start方法开启线程。
注意:一个线程一旦开启,那么线程就会执行run方法中的代码,run方法千万不能直接调用,直接调用run方法就相当于调用了一个普通的方法而已,并没有开启新的线程。
方式二:1、自定义一个类实现Runnable接口;2、实现runnable接口的run方法,把自定义线程的任务定义在run方法上;3.创建runnable实现类的对象;4、创建Thread累的对象,并且把Runnable实现类的对象作为实参传递;5、调用Thread对象的start方法开启一个线程。
注意:Runnable实现类的对象并不是一个线程对象,只不过是实现了Runnable接口的对象而已;只有是Thread或者是Thread的子类才是线程对象。
问:为什么要把Runnable实现类的对象作为实参传递给Thread对象呢?作用是什么?
答:作用就是把Runnable实现类的对象的run方法作为了线程的任务代码去执行了。
推荐使用第二种线程生成方式。
线程的生命周期图:
运行状态下的线程一旦执行了sleep或者是wait方法之后,那么该线程会进入临时阻塞状态下,如果线程是调用了sleep方法进入的临时阻塞状态,那么线程一旦超过了指定的睡眠时间,那么就会重新进入可运行状态,如果嗲用了wait方法进入的临时阻塞状态,那么需要其他线程唤醒该线程才可以重新进入可运行状态。
线程常用的方法:
Thread(String name) 初始化线程的名字
getName() 返回线程的名字
setName(String name)设置线程对象名
sleep()线程睡眠指定的毫秒数,sleep的异常类型只能捕获,不能抛出,因为Thread类的run方法没有抛出异常类型,所以里面的sleep的InterruptedException只能使用try捕获。
getPriority() 返回当前线程对象的优先级 默认线程的优先级是5
setPriority(int newPriority)设置线程的优先级,虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现(最大的优先级是10 , 最小的是1 ,默认的是5)
currentThread() 返回当前的线程对象,该方法是一个静态方法。注意:哪个线程执行了currentThread()代码就返回那个线程的对象
线程安全问题:
在什么情况下才可能出现线程安全问题:
1、存在两个或者两个以上的线程对象,而且线程之间共享着一个资源;
2、有多个语句操作了共同资源;
线程安全问题的解决方案:sun提供了线程同步机制让我们解决这类问题的。
Java线程同步机制的方式:
方式一:同步代码块
同步代码块的格式:
synchronized(锁对象) {
需要被同步的代码…
}
同步代码块要注意的事项:
1、任意的一个对象都可以作为锁对象;凡是对象内部都维护了一个状态的,Java同步机制就是使用了对象中的状态作为了锁的标识。
2、在同步代码块中调用了sleep方法并不是释放锁对象的。
3、只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率的。
4、多线程操作的锁对象必须是唯一共享的,否则无效(不能在括号里new Object())。
方式二:同步函数
同步函数就是使用sinchronized修饰一个函数。
同步函数要注意的事项:
1、如果是一个非静态的同步函数的锁对象是this对象,如果是静态的同步函数的锁对象是当前函数所属的类的字节码文件(class对象)。
2、同步函数的锁对象是固定的,不能由你来指定的。
推荐使用:同步代码块。
原因:1.同步代码块的锁对象可以有我们随意指定,方便控制。同步函数的锁对象是固定的,不能有我们来制定。2.同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数的所有代码都被同步了。
死锁
每个人都拥有其他人需要的资源,同时又等待其他人拥有的资源,并且每个人在获得所有需要的资源之前都不会放弃已经拥有的资源。
当多个线程完成功能需要同时获取多个共享资源的时候可能会导致死锁。
死锁现象出现的根本原因:
1、存在两个或者两个以上的线程;
2、存在两个或者两个以上的共享资源;
线程的通讯
线程通讯:一个线程完成了自己的任务时,要通知另外一个线程去完成另外一个线程
wait():等待 如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用 notify方法才能唤醒。
notify():唤醒 唤醒等待的线程的其中的一个。
notifyAll():唤醒线程池中所有等待的线程。
wait与notify方法要注意的事项:
1、wait方法与notify方法是属于Object对象的;
2、wait方法与notify方法必须要在同步代码块或者是同步函数中才能使用;
3、wait方法与notify方法必须要由锁对象调用。
wait和notify方法的原理:
wait():一个线程如果执行了wait方法,那么该线程就会进去一个以锁对象为标识符的线程池中等待。
notify():如果一个线程执行notify方法,那么就会唤醒以锁对象为标识符的线程中等待线程中其中一个。
线程的停止:
stop()方法虽然可以使用作用,但是已经过时了。
interrupt()方法根本无法停止一个线程。中断线程,意思是将临时阻塞的状态给清除掉(县城在wait、join、sleep中),把线程的等待状态给强制清除,被清除的状态会接到一个interrupt异常。
线程的停止:
1、停止一个线程,我们一般都会通过一个变量去控制的。方法就是让他执行完,变量控制停止循环。
2、如果需要停止一个处于等待状态下的线程,我们需要通过变量配合notify方法(变量置为false后唤醒该线程就会结束循环了,记住唤醒的操作应该是在同步代码块synchronized中)或者是interrupt()来使用。
后台线程:
守护线程(后他线程):如果一个进程中只剩下了守护线程,那么守护线程也会死亡。
判断一个线程是不是守护线程的方法:线程名.isDaemon()。
设置线程是否为守护线程,true为守护线程,false为非守护线程:线程.setDaemon(true/false)。
join方法
加入线程。一个线程如果执行join()语句,那么就有新的线程加入,执行该语句的线程必须要让步给新加入的线呈先完成任务,然后才能继续执行。