多线程
一.概念
1. 进程:进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
2. 线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。且线程只能属于一个线程。
3.线程是一种轻量级的进程。
4.线程没有地址空间,线程包含在进程的地址空间中。
5.多线程三大特性: 原子性,可见性,有序性。
二. Java线程具有的5种状态
新建状态(NEW):当线程对象被创建后,即进入新建状态。【Thread t=new Thread()】
就绪状态(Runable):当线程对象调用了start方法,即进入就绪状态,等待cpu调度运行【t.start()】
运行状态(Running) :就绪状态的线程,被cpu调度即,进入运行状态
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时线程进入阻塞状态;
三种阻塞状态
A.等待阻塞;运行状态中线程执行了wait()方法,使得本线程进入阻塞状态
B.同步阻塞;线程在获取synchronized同步锁(锁被其他线程锁占有),进入同步阻塞状态
C.其他阻塞:线程调用了sleep(),join()或者发出I/O请求时,线程会进入阻塞状态,当sleep超时,join等待线程终止或者超时,I/O请求完成,线程会进入就绪状态
死亡状态(Dead): 线程执行完了,或者因异常退出run()方法。
三.线程属性
1.线程优先级
线程范围:0-10;默认优先级为5;高优先级的线程先执行。
默认情况下,线程是继承父类的优先级;也可通过setPriority()修改优先级。
2.用户线程和守护线程
用户线程:非守护线程,常规的线程
守护线程:作用是为其他线程提供服务。只要当JVM还有用户线程,那么守护线程就会一直工作下去。例如:垃圾回收期;
程序编写者可以设置守护线程;方法: t.setDaemon(true)
四.线程创建(NEW和Runable状态)
1.继承Thread类,重写run()方法
package com.demo9aa.demo7;
public class ThreadA extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
for(;i<10;i++){
System.out.println(i);
}
}
public static void main(String[] args) {
ThreadA a=new ThreadA();//创建ThreadA;new状态
a.start();//runable状态
}
}
2.实现Runable接口,并重写该接口的run()方法
package com.demo9aa.demo7;
public class ThreadB implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
int i=0;
for(;i<10;i++){
System.out.println(i);
}
}
}
3.使用Callable和Future接口创建线程。
A.创建Callable接口的实现类,并实现call()方法。
B.使用FutureTask类来包装Callable实现类的对象,
C.此FutureTask对象作为Thread对象的target来创建线程。
package com.demo9aa.demo7;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Demo1 {
public static void main(String[] args) {
//A
Callable<Integer> c=new CallAbleA();
//B
FutureTask<Integer> f=new FutureTask<>(c);
//C
Thread r=new Thread(f);
r.start();
}
}
class CallAbleA implements Callable<Integer>{
int i=0;
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
for(;i<10;i++){
System.out.println(i);
}
return i;
}
}
五.线程让步(yield方法)
从运行态转换为就绪态有两种情况 1.时间片用完。2.线程主动调用yield()
yield方法作用:主动归还资源,使自己在就绪态
六.其他阻塞(I/O请求,sleep,join)
I/O请求:当线程开始进行I/O操作时,线程进入阻塞状态。I/O操作完成后,线程进入就绪态
join函数: 让一个线程等待另一个线程完成,才可以继续执行【在子线程未完成或者未超时之间,父线程进入阻塞状态;子线程完成或者超时,父线程进入就绪态】
sleep函数:让当前正在执行的线程,暂停制定时间,并进入阻塞状态【在睡眠时间,线程进入阻塞状态,超时后,恢复到就绪态】
七.等待阻塞和同步阻塞
A:等待阻塞
当正在运行时的线程(已获得锁),调用wait()方法时,线程主动放弃锁,此时线程进入 等待阻塞状态。
B: 同步阻塞
当正在运行的线程(未获得锁),获得锁失败(锁被其他线程锁占用),线程进入同步阻塞阶段。
当运行线程,调用notify和notifyAll方法;此时唤醒“等待阻塞的线程“进入同步阻塞阶段。
同步阻塞的线程,一旦获得锁,就进入就绪态。
八.线程中断
8-1.历史方法: Thread有成员方法:suspend和stop都用于中断线程。 问题? ?? 线程强制中断,会导致锁永远无法归还。
8-2.最新做法: 使用等待/通知或者给哪个线程一个中断信号,让他自己决定中断。
8-2-1.涉及方法:
interrupted()方法: 静态方法;查看当前中断信号,并其清除中断信号
isInterrupted()方法: 查看当前中断信号。
interrupt()方法:修改被调用线程的中断状态,告知那个线程,他需要被中断。
8-2-2.详细操作如下:
对于非阻塞线程:修改了中断状态【Thread.isInterrupted返回true】,无任何特殊现象
对于可取消的阻塞状态的线程中:(wait,sleep,join)
A 线程收到中断信号后,会抛出InterruptedException;
B 抛出异常后会把中断状态置为true
对于不可中断阻塞的线程中:(I/O请求时):
线程收到中断信息后,,他们不会抛出InterruptedException,且不会因为中断情况,而退出阻塞。
注意:
尝试获取一个内部锁的操作(进入一个 synchronized 块)是不能被中断的; 但是 ReentrantLock 支持可中断的获取模式即 tryLock(long time, TimeUnit unit)。