文章说明:黑色字体-已知,蓝色字体-所见即所得,红色字体-还需要再找资料弄明白的,黄色背景-提炼关键信息
参考资料:很多参考资料,段落下有原文链接到原作者
简介
Process(程序)、Thread(线程)、进程。
线程状态
1. 新建(new):当线程被创建时,分配了系统资源,执行了初始化。有资格获得CPU 时间,调度器会把这个线程转变为运行状态或阻塞状态。还没调用start方法。
2. 就绪(Runnable):可执行,是否执行取决于调度器。不同于死亡和阻塞状态。
3. 阻塞(Blocked):线程试图获取一个对象锁,如果该对象锁被其他线程所持有,进入阻塞状态。线程处于阻塞状态时,调度器忽略此线程,不分配给线程CPU 时间。直到线程持有锁进入就绪状态。
4. 无限等待(Waiting):一个线程在等待另一个线程执行(唤醒)动作时,该线程进入无限等待状态。不能自动唤醒,必须等待另一个线程调用notify或notifyAll方法唤醒。
5. 计时等待(TimedWaiting):同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直等到超时期满或者接收到唤醒通知。带有超时参数的常用方法有:Thread.sleep()、Object.wait()
6. 被终止(Teminated):因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
7. 死亡 (Dead):处于死亡或者终止状态的线程将不可调度,再也不会得到CPU 时间,它的任务已经结束,或不再可运行。线程死亡的通常方式是从run() 方法返回,但是线程任务还可以被中断。
创建线程
创建线程的三种方法
方法一:继承 Thread 类,重写 Thread 的 run 方法,在主线程类种创建自定义线程类对象,调用 start 方法开启线程。run 方法相当于其他线程的 main 方法。
//自定义线程类继承Thread。
public class myThread extends Thread {
//重写run方法,完成该线程执行的逻辑。
public void run()
{
for(int i = 0;i < 50;i++)
{
System.out.println("新线程正在执行" + i);
}
}
}
//主线程类。
public static void main(String[] args)
{
//创建自定义线程对象。
myThread mT = new myThread();
//开启新线程,让新的线程执行程序,jvm调用线程中的run。
mT.start();
//在main方法中执行。
for(int i = 0;i < 50;i++)
{
System.out.println("main线程正在执行" + i);
}
}
方法二:实现 Runnable 接口,重写 run 方法,在主线程类种创建自定义实现类对象,将对象传入到Thread对象的构造方法中,Thread对象调用start方法开启线程。
//定义接口实现类
public class myRunnable implements Runnable {
//重写run方法
public void run()
{
for(int i = 0;i < 5;i++)
{
System.out.println("myRunnable线程正在执行!");
}
}
}
//主线程类。
public static void main(String[] args)
{
//创建线程执行目标类对象
myRunnable mR = new myRunnable();
//将Runnable接口的子类对象作为参数传递给Thread类的构造函数
Thread t1 = new Thread(mR);
Thread t2 = new Thread(mR);
//开启线程
t1.start();
t2.start();
for(int i = 0;i < 5;i++)
{
System.out.println("main线程正在执行!");
}
}
方法三:Callable 接口。
停止线程
stop和destroy方法已过时,不建议使用。
设置标示位boolean flag,循环判断flag状态,通过更改flag状态终止循环停止线程。
快捷键预警:写for循环的时候可以1000.for直接出现写好的判断代码。(IDEA)
线程名称的获取和设置
String getName():返回该线程的名称(同 Thread.currentThread().getName())
Thread.currentThread():获取当前线程对象
Thread.currentThread().getName():获取当前线程对象的名称
setName():设置线程名称
public static void main(String[] args)
{
myThread mT = new myThread();
mT.start();
mT.setName("thread1"); //设置线程名称
//System.out.println(getName()); //使用getName()方法获取线程名称 在新线程中可以这样使用
System.out.println(Thread.currentThread().getName()); //使用Thread.currentThread().getName()获取线程名称
System.out.println(Thread.currentThread()); //获取当前线程对象
}
一些方法
1、Thread.sleep(long millis):休眠,单位毫秒。
public static void main(String[] args) {
myThread mT = new myThread();
mT.start();
//使用sleep方法延时打印for循环
for(int i = 0;i < 5;i++)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
2、wait():等待。
3、setPriority(int newPriority):更改线程优先级。
4、join():等待该线程终止。
5、yield():暂停当前正在执行的线程对象,并执行其它线程。
6、interrupt():中断线程,不要使用。
7、isAlive():测试线程是否处于活动状态。
等待和唤醒机制
等待唤醒机制是为了方便处理进程之间通信的手段,多个线程在处理同一个资源时,由于处理的动作(线程的任务)不行同,为了使各个线程能够有效的利用资源,便采取了等待唤醒机制。
等待唤醒机制涉及到的方法:
- wait():等待。将正在执行的线程释放其执行资格和执行权,并存储到线程池中
- notify():唤醒。唤醒线程池中被 wait() 的线程,一次唤醒一个,而且是任意的
- notifyAll():唤醒全部。可以将线程池中的所有 wati() 线程都唤醒
注:
- 这些方法都是在同步中才有效,在使用时必须注明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程
- 因为这些方法在使用的时候要注明所属的锁,而锁又是任意对象,所以这些方法是定义在 Object 类中的
分析:
- 输入 inPut 类:输入完成后,必须等待输出结果打印结束才能进行下一次赋值,赋值后,执行wait()方法永远等待,直到被唤醒,唤醒后重新对变量赋值,赋值后再唤醒输出线程 notify(),自己再wait()
- 输出 outPut 类:输出完成后,必须等待输入的重新赋值后才能进行下一次输出,在输出等待前,唤醒输入的notify(),自己再 wait() 永远等待,直到被唤醒
线程池
是一个线程容器,实现了线程的复用,不必反复创建与销毁。
创建线程池的两种方法
方法一:Runnable接口
通过线程池工厂创建,调用线程池中的方法获取线程,通过线程执行任务方法。
1、线程池创建工厂类:public static ExecutorService newFixedThreadPool(int nThreads)返回线程池对象。
2、线程池类:Future<?> submit(Runnable task)获取线程池中的某一个线程对象并执行。Future 接口:用来记录线程任务执行完毕后产生的结果。
3、使用线程池中的线程对象:创建线程池对象、创建 Runnable 接口子类对象、提交 Runnable 接口子类对象、关闭线程池。
//Runnable接口实现类
public class myRunnable implements Runnable {
@Override
public void run() {
for(int i = 0;i < 5;i++)
{
System.out.println("myRunnable正在执行!");
}
}
}
//主线程中调用
public static void main(String[] args)
{
//创建线程池对象
ExecutorService ex = Executors.newFixedThreadPool(2);
//创建Runnable实例对象
myRunnable mR = new myRunnable();
//从线程池中获取线程对象,调用myRunnable中的run(),没有返回值
ex.execute(mR);
//再获取个线程对象,调用myRunnable中的run(),有返回值
ex.submit(mR);
//关闭线程池
ex.shutdown();
}
注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中。
方法二:Callable接口
由于Runnable 接口中发 run 方法是 void 类型,无返回值,并且不能抛异常,而Callable 接口中的 call 方法弥补了这两点缺陷,有返回值,并能抛异常,用法和 Runnable 接口一样。
1、线程池创建工厂类:public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象。
2、线程池类:<T> Future<T> submit(Callable<T> task)获取线程池中的某一个线程对象,并执行线程中的call()方法。Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。
3、使用线程池中线程对象的步骤:创建线程池对象、创建Callable接口子类对象、提交Callable接口子类对象、关闭线程池。
//Callable接口实现类,call方法可抛出异常、返回线程任务执行完毕后的结果
public class myCallable implements Callable {
@Override
public Object call() throws Exception {
System.out.println("myCallable线程!");
Thread.sleep(1000);
System.out.println("正在执行");
return null;
}
}
public static void main(String[] args)
{
//创建线程池对象
ExecutorService ex = Executors.newFixedThreadPool(2);
//创建Callable实例对象
myCallable mR = new myCallable();
//从线程池中获取线程对象,然后调用myCallable中的call()
ex.submit(mR);
//再获取个线程对象,调用myCallable中的call()
ex.submit(mR);
//关闭线程池
ex.shutdown();
}
原文链接:https://blog.youkuaiyun.com/One_L_Star/article/details/95985907
原文链接:https://blog.youkuaiyun.com/One_L_Star/article/details/96171934