1.认识多线程
线程
线程存在于进程的内部,是比进程还小的单为。进程是系统分配资源的最小单位,线程是系统调度的最小单位。一个进程之内的线程之间是可以共享资源的,每一个进程至少存在一个线程,即主线程。
多线程的作用
在确保安全的情况下,尽可能提高系统效率,尽可能利用系统资源。
使用场景
创建线程是比较耗费时间和资源的,所以要综合考虑同时执行指令(任务量)+创建线程数+系统可利用资源(内存,CPU)
一般在一个时间点要同时做多件事(执行多行代码)时就可以使用多线程。
内部类
匿名内部类:在一个方法中,new类型/接口(){ } ()指定构造方法,{ }指定要重写的方法
静态内部类:定义在另一个类的内部的类,作为另一类的静态成员,通过 类名.静态成员名 来访问。
public class 匿名内部类 {
public static void main(String[] args) {
A a = new A(){//匿名内部类,本质是A的子类,但不能等同A类型
//可以重写方法,或不重写(继承)
@Override
public void pro() {
System.out.println("pro");
}
};
a.pro();//想打印pro
B b = new B() {//匿名内部类:B接口的实现类,不能等同于B
@Override
public void pro() {
System.out.println("pro");
}
};
b.pro();//打印pro
}
//静态内部类
static class A{
public void pro(){
System.out.println("a pro");
};
}
//内部接口
interface B{
void pro();
}
}
创建线程的方法
- 继承 Thread 类
可以通过继承Tread来创建一个线程,该方法的好处是this代表的就是当前的线程,不需要Tread.currentTread()来获取当前进程的引用。
class MyThread extents Thread{
@Override
public void run(){
System.out.println("这是线程运行的代码")
}
}
MyThread t=new Mytraed();
t.start();线程开始运行
- 实现Runable接口
通过实现Runnable接口,并且调用Tread 的构造方法时,将Runnable对象作为target参数传入来创建线程对象。该方法的好处是可以规避类的单继承的限制,但需要通过Thread.currentThread()来获取当前线程的引用
class MyRunnable implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+"这是线程运行的代码")
}
}
Thread t=new Thread(new MyRunnable())
t.start();
- 其他变形
使用匿名类创建Thread 子类对象
Thread t1=new Thread(){
@Override
public void run(){
System.out.println("使用匿名内部类创建Tread的子类对象");
}
};
使用匿名类创建Runable子类对象
Thread t2=new Thread(new Runnable(){
@Override
public void run(){
System.out.println("使用匿名类创建Runnable子类对象");
}
});
Thread类及常见API
Thread类是JVM用来管理线程的一个类,每一个线程都有唯一的Thread对象与之关联。
Thread类的构造方法
- Thread(),创建线程对象
- Thread(Runnable target),使用Runnable对象创建线程对象
- Thread(String name),创建线程并命名
- Thread(Runnable target,String name),使用Runnable对象创建线程,并命名
Thread的几个常见属性
- 属性:ID,获取方法:getId()
- 属性:名称,获取方法:getName()
- 属性:状态,获取方法:getState()
- 属性:优先级,获取方法:getPriority()
- 属性:是否是后台线程,获取方法:isDamon()
- 属性:是否存活,获取方法:isAlive()
- 属性:是否被中断,获取方法:isInterrupted()
启动线程
start:线程启动的方法,启动之后才表现并发,并行的特性。
申请系统调度线程,让CPU执行(创建态——>就绪态),如果线程获取CPU时间片,就开始执行任务(Thread类中run方法和Runnable对象中run,就是任务的定义)
void start();//导致此线程执行,Java虚拟机调用此现成的run方法。
线程中断
- 通过共享的标记来进行沟通
public class ThreadDemo{
private static class MyRunnable implements Runnable{
public volatile boolean isQuit=false;
@Override
public void run(){
while(!isQuit){
System.out.println(Thread.currentThread().getName()+"正在执行线程")
try{
Thread。sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"收到消息,线程已经停止")
}
}
public static void main(String[] args)throw InterruptedException{
Mythread target=new Mythread();
Thread thread=new Thread(target,"A")
System.out.println(Thread.currentThread().getName()+"让A线程开始执行");
thread.start();
Thread.sleep(10*1000);
System.out.println(Thread.currentThread().getName()+"让线程A停止执行"
target.isQuit=true;
}
}
当标志位 isQuit 置为true之后,线程Arun方法退出while循环,停止线程运行。
- 通过调用interrupt()方法来通知,初识值=false(没有被中断)
public class ThreadDemo{
private static class MyRunnable implements Runnable{
@Override
public void run(){
while(!Thread.interrupted()){
System.out.println(Thread.currentThread().getName()+"正在执行线程")
try{
Thread。sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"收到消息,线程已经停止")
}
}
public static void main(String[] args)throw InterruptedException{
Mythread target=new Mythread();
Thread thread=new Thread(target,"A")
System.out.println(Thread.currentThread().getName()+"让A线程开始执行");
thread.start();
Thread.sleep(10*1000);
System.out.println(Thread.currentThread().getName()+"让线程A停止执行"
thread.interrupt();
}
}
中断某个线程
(1)线程中断标志位=true
(2)线程要不要中断,线程定义的代码任务自行决定
(3)如果线程处于阻塞状态(调用多线程阻塞API方法,显示抛出InterruptException的方法),直接提前让线程从阻塞状态转变为就绪态,在系统调度执行后,以抛出异常的方式继续执行
线程等待
有时,我们需要等待一个进程完成工做后,才能进行自己的下一步工作。
API方法:
(1)无参方法,就是线程执行完毕
(2)有参方法:是线程执行完毕和时间到达任意一个满足,满足等待时间后继续往下执行
线程让步
暂停当前线程对象(交出CPU权限,让CPU去执行其他的线程),并执行其他线程,但是不会让线程进入阻塞态,而是让线程返回就绪态
获取当前线程引用
线程状态
Java中线程的状态
- NEW
- RUNNABLE(可运行态=就绪态+运行态)
- BLOCKED
- WAITING
- TIMED_WAIT
- TERMINATED
线程池
-
线程池的作用:不用每次执行任务时,都要创建进程(会真实创建系统级别的线程,线程的创建与销毁都很耗时,而是可以使用线程池中的线程来复用,减少每次启动、销毁线程的开销
-
线程池的重要参数
(1)corePoolSize:核心线程数
(2)maximumPoolSize:最大线程数
(3)keepAliveTime:非核心线程的空闲时间,超过KeepAliveTime就会自动被回收
(4)TimeUnit:空闲时间KeepAliveTime的时间单位
(5)workQueue:用于保存任务的队列,可以为有界、无界同步队列
(6)ThreadFactory:创建线程的工厂类,默认使用Excutors.defaultThreadFactory()
(7)RejectedExcutionHandler(拒绝策略):线程池已经满了(保存任务的队列已满),不能再接收任务时采取拒绝策略处理。 -
常用线程池创建
1、自动创建
-
newCachedThreadPool:初始化一个缓存线程池,不会对线程的数量做出限制,最大线程数完全依赖于操作系统
-
newFixedThreadPool:初始化一个指定数量的线程池,corePoolSize==maxiPoolSize,使用LinkedBlockingQueue作为阻塞队列
-
newSingleThreadExecutor:初始化只有一个线程
-
newScheduledThreadPool:创建一个定时调度的线程池,内置延迟队列,
2、手动创建
ExecutorService pool = new ThreadPoolExecutor
(传入各参数的的值(核心线程数、最大线程数等));
- 停止线程池
(1)shutDown():中断队列中的任务,工作线程正在执行的任务还是执行,等执行完毕才关闭线程
(2)shutDownNow():中断队列中的任务,及工作线程正在执行任务,然后关闭线程池。