线程
1.三种线程
1.继承Thread
main方法也是一个线程
-
新建线程:新建类继承Throw,重写run方法
public class Test() extends Throw(){ public void run(){ 线程需要执行的内容; } }
-
创建线程:
Test test = new Test();
-
启动线程:创建一个新的栈,而不是创建栈帧
test.start();
2.实现Runnable
-
新建线程:新建线程类,实现Runnable接口,重写run方法
public class Test implements Runnable{ public void run(){ 线程内容; } }
-
创建线程对象
Test test = new Test();
-
新建线程
Thread thread = new Thread(thread[,"线程名"]);
-
执行线程
thread.start();
3.使用Callable和Future
-
新建线程:新建范方法线程类,实现Callable类,重写call方法
可以抛出异常而不需要必须在县城里处理public class Test implements Callable<call方法返回值>{ public Integer call() throws Exception{ 线程内容; } }
-
新建线程对象
Test test = new Test(); FutureTask<Integer> result = new FutureTask<>(test);
-
执行线程
Thread thread = new Thread(result);
-
执行线程
therad.start();
-
返回值:get()会阻塞到结束时执行
try{ Integer sum = result.get(); }catch(){}
2.相关内容
1.匿名内部类
-
Thread
Thread thread = new Thread(){ public void run(){ } } thread.start();
-
Runnable
Thread thread = new Runnable(){ public void run(){ } } new Thread(thread.start());
2.Thread对象
-
获取线程对象(静态)
-
run方法内
只适用于实现Runnable或Callable的接口的run方法内执行
不能使用this,this指的是线程类这一个对象,而静态方法获取的是不同的三个对象
Thread thread = Thread.currentThread();
-
线程外获取对象
Test test = new Test(); Thread thread = new Thread(test);
-
-
获取线程名
String name = thread.getName();
-
获取线程id
Int id = thread.getId()
-
获取线程优先级[见下文]
thread.getPriority();
-
检查是否处于活动状态[见下文]
线程处于:就绪/运行/阻塞返回
true
,处于新建/结束返回flase
;thread.isAlive();
-
检查是否为守护线程[见下文]
thread.isDaemon();
-
检查是否被中断
thread.isInterrupted();
3.优缺点
建议使用Runnable或Callable
- 由于java里一个类只能继承一个父类,所以Thread只能继承一个父类,而runnable和callable可以继承多个父类
- runable和callable可以实现代码的分离,面向对象
- runable和callable代码复杂,获取线程对象要用Thread.currentThread()方法,而thread可以直接对象点;
3.生命周期
-
图表
状态 线程 新建 使用new关键字创建线程后 就绪 线程对象调用start()方法后 运行 获得cpu资源,执行run方法体后 死亡 run()结束/错误/执行stop() -
图示
线程从阻塞状态只能进入就绪状态
3. 注意事项
-
主线程和子线程:
主线程和子线程有相同的地位,子线程不收主线程的影响,主线程结束后子线程可以继续执行;
-
start()
对新线程两次调用start()或者对死亡的线程调用start()都会抛出异常
illegalThreadStateException
调用run()方法以后不能再次调用start()方法会抛出异常
4.控制线程
-
新建线程:
Thread/Runnable/Callable
Test test = new Test(); Thread thread = new Thread(test);
1.join
因为阻塞后会处于就绪,因此.start()可以解除join
-
在therad2线程中调用thread.join()方法,therad2会被一直阻塞,直到therad执行完毕
thread.join();
-
在therad2线程中调用thread.join(n毫秒)方法,therad2会被一直阻塞n毫秒
thread.join(n);
2.后台线程(守护线程)
- 注意事项:
- 当前台线程全部执行完毕,守护线程也随之结束,jvm的gc就是守护线程,
- 线程处于:就绪/运行/阻塞返回
true
,处于:新建/结束返回flase
; - 前台线程创建的默认是前台线程,后台线程创建的线程默认是后台线程;
-
设置为后台线程
必须在thread.start()之前执行,否则返回
illegalThreadStateException
thread.setDaemon(true);
-
获取当前线程状态
boolean flag= thread.getDaemon();
3.sleep(线程休眠)
可能发生死锁,尽量少使用,run方法内执行
-
让thread线程休眠
Thread.sleep();
-
让thread休眠n毫秒(收系统精度影响)
Thread.sleep(n);
4.yield(线程让步)
移植性低,不建议使用,建议使用sleep
会将线程强制转换到就绪状态,而不是阻塞,在执行后会暂停并给大于等于自身优先级的线程(包括)执行机会,
Thread.yield();
5.设置优先级
每个线程默认的优先级都与创建它的父线程优先级相同;
-
优先级级别
-
范围从1-10,但是由于要受操作系统的影响,所以应该尽量使用常量优先级
-
常量优先级
常量 级别 Thread.MAX_PRIORITY 10 Thread.MIN_PRIORITY 1 Thread.NORM_PRIORITY 5(main)
-
-
获取优先级
int x = thread.getPriority();
-
设置优先级
thread.setPriority(优先级);
5.线程同步
-
并发问题的原因
int total2=2000 if(total>total2){ //1 syso("取钱成功:"); total2=total2-1500; //2 }
如果线程1在经过1时正好堵塞,线程2在此期间执行完毕,这时线程1再执行完毕则出现错误;也就是说有同时执行1的可能
-
同步代码块
参数是同步监视器:当一个线程使用
obj
时阻止其他线程使用,直到代码块执行完毕- 同步监视器:obj,需要竞争的对象资源
- 同步代码块:需要执行的操作
synchronized(obj){ //加锁 同步代码块 //修改 } //释放锁
-
同步方法
被synchronized修饰的方法,默认同步监视器是this就是当前对象;
public synchronized void XXX(){ 同步代码块 }
-
同步监视器
- 释放同步监视器的锁定
- 同步方法或代码块执行结束
- 遇到break/return提前结束同步方法或代码块
- 出现未处理的error或Exption导致同步放那个发或代码块结束
- 执行了同步监视器对象的wait()方法,使当前线程暂停会释放监视器
- 不释放同步监视器的锁定
- Thread.sleep()/Thread.yield()
- suspend()将线程挂起
- 释放同步监视器的锁定