多线程基础学习笔记
了解多线程
进程与线程
进程是一个电脑上的程序,线程是进程中的执行单位
进程拥有许多线程与堆、方法区,线程拥有自己的栈、程序计数器、本地方法栈
并发与并行
并发:同一时刻多个任务都执行
并行:同一时间段多个任务执行
为什么要使用多线程
单处理器:io操作与cpu操作不能同时运行,多线程可以提高程序运行效率
多处理器:使用单线程无法同时利用所有cpu
服务器:需要同时响应多个用户请求
使用多线程需要处理的问题
1,线程安全
2,死锁
3,内存泄漏
多线程实现方式
继承Thread类
1,构造Thread子类,重写run方法
2,创建该子类实例对象,调用start方法
特点:java只能继承一个父类
为什么不直接使用run方法
执行run只能运行里面的代码,start才能启动一个线程
实现Runnable接口
1,构造Runnable接口子类实例对象,重写run方法
2,创建该子类实例对象
3,调用有参的Thread构造方法,调用start方法
特点:java可以实现多个接口,避免单继承的局限性
更好的处理共享资源的情况
实现Callable接口
1,构造Callable接口子类实例对象,重写call方法
2,创建该子类实例对象
3,调用有参的FutureTask构造方法
4,调用有参的Thread构造方法,调用start方法
特点:有返回值,可声明抛出异常
代码:
public class Test2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Thread1 t1 = new Thread1();
t1.start();
Thread2 t2 = new Thread2();
new Thread(t2).start();
Thread3 t3 = new Thread3();
FutureTask<Object> f = new FutureTask<>(t3);
Thread t = new Thread(f);
t.start();
System.out.println(f.get());
}
}
class Thread1 extends Thread{
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
class Thread2 implements Runnable{
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
class Thread3 implements Callable<Object>{
public Object call() throws Exception {
// TODO Auto-generated method stub
for(int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
return 10;
}
}
线程的生命周期
新建
对象创建时
可运行
调用start方法后变为可运行状态
分为就绪与运行
阻塞
一般与锁有关
等待获取锁时发生阻塞
发出IO请求时也会发生
等待
使用无参的join、wait等方法会发生等待
其他线程使用notify、notifyAll后线程进入就绪
定时等待
使用有参的sleep、wait、join会定时等待
其他线程使用notify、notifyAll后线程进入就绪
时间到也会进入就绪
死亡
执行完毕,或者抛出异常,错误时线程死亡
死亡后无法进入其他状态
线程调度
强占调度:按优先级争取CPU资源
定时调度:线程获得定时大小的时间片并执行
优先级
高优先级容易抢到CPU资源
优先级分为1到10
优先级改变使用setPriority方法
让步
线程进入就绪状态,放弃cpu资源
使用yield方法
休眠
使用sleep方法使线程休眠
插队
A线程插入B线程中,只有A运行结束才能运行B
使用join方法
后台
线程分前台与后台,默认情况下为前台
前台线程全部结束,程序结束
需要在start方法使用前使用setDeamon方法
多线程同步
线程安全
有延时时会引发安全问题,与数据库类似
同步代码块
使用synchronized方法锁住一个对象,以下代码块同步
对象任意,但必须相同,代码块未执行时标记值为1,使用时为0,为0时其他线程无法执行代码块
同步方法
用synchronized修饰方法,被修饰方法同步
方法被锁时上锁的对象为调用方法的对象
同步静态方法
不创建对象,同步静态方法的锁为该方法的class对象
synchronized缺点:每次线程调用时都需要判断锁的标记值,消耗大
线程需要一直等待直到拿到锁
同步锁
使用Lock接口、ReentrantLock实现类构造锁,使用lock方法与unlock方法可以实现线程同步
一般在finally中写unlock方法
线程多次申请取锁失败后,不再等待,较为灵活
多线程通信
wait与sleep的区别:
sleep使用者为线程,wait使用对象为同步锁对象
wait放弃锁对象,sleep不放弃
使用wait线程不会自动苏醒,需要notify、notifyAll。sleep方法(和有参的wait)执行完成后,线程会自动苏醒。
notify、notifyAll的区别
notify唤醒同步锁上第一个等待的线程(第一个调用wait的线程),notifyAll唤醒所有等待的线程