1.线程与进程的区别
进程其实就是一个应用程序,进程是所有线程的集合,每个线程是进程中的一条执行路径。
进程之间不能共享数据,但线程可以。
系统创建进程需要重新分配系统资源,创建线程代价比较小。
java语言内置了多线程功能支持,简化了java多线程编程。
2.为什么要使用多线程
多线程能提高程序的效率
3.多线程应用场景
主要能体现多线程提高程序效率 比如迅雷多线程下载、分批发送短信。
4.多线程的创建方式
继承Thread类
public class ThreadDemo01 {
public static void main(String[] args) {
//实例化Thread子类,创建线程对象
ExtendsThread extendsThread = new ExtendsThread();
//调用线程对象的start方法 不能直接调用run方法
extendsThread.start();
}
}
//定义一个继承Thread的子类 并重写run方法
class ExtendsThread extends Thread{
//run方法里是线程需要执行的任务 执行的代码
public void run(){
}
}
实现Runnable接口
public class ThreadDemo02 {
public static void main(String[] args) {
//先创建Runnable实现类的实例
ImplementsRunnable implementsRunnable = new ImplementsRunnable();
//不能直接调用implementsRunnable的run方法
//如果直接调用run方法的话 并没有创建线程 仍然是在主线程中
//需要实例化一个Thread 并把implementsRunnable作为Thread的target对象
Thread thread = new Thread(implementsRunnable);
//然后调用Thread的start方法启动线程
thread.start();
}
}
//定义一个实现Runnable接口的实现类 重写run方法
class ImplementsRunnable implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
}
}
匿名内部类方式
public class ThreadDemo03 {
public static void main(String[] args) {
/**
* 匿名内部类指的是没有名字的内部类 正因为没有名字,匿名内部类只能使用一次
* 使用匿名内部类有一个条件:必须继承一个父类或者实现一个接口
* 匿名内部类不能定义任何静态成员、方法
* 匿名内部类的方法不能是抽象的
* 匿名内部类访问外部类成员变量或成员方法必须用static修饰
* 此处我们使用匿名内部类创建多线程
*/
//1.继承Thread父类 重写run方法
new Thread(){
public void run(){
}
}.start();
//2.实现Runnable接口 重写run方法
new Thread(new Runnable() {
public void run() {
}
}).start();
}
}
5.为什么不能直接调用run方法 要调用start方法
如果在主线程main方法里直接调用run方法的话,那就没有创建线程,是一个单线程。代码依然是顺序执行。
6.创建线程是继承Thread类好还是实现Runnable方法好
实现Runnable方法更好,因为继承只能继承一个,而实现可以实现多个方法。实现了接口还能继续继承。继承了类就不能再继承。
jdk1.8中文api文档
7.常用线程api方法
start() | 启动线程 |
currentThread() | 获取当前线程名称 |
getId() | 获取当前线程编号 |
getName() | 获取线程名称 Thread-编号 从0开始 |
sleep(long mill) | 休眠线程 |
stop() | 停止线程 |
8.常用线程构造函数
Thread() | 分配一个新的Thread对象 |
Thread(String name) | 分配一个新的Thread对象 具有指定的name |
Thread(Runable r) | 分配一个新的Thread对象 |
Thread(Runable r,String name) | 分配一个新的Thread对象 |
9.多线程运行状态
线程从创建、运行到结束,一共有五个状态。新建、就绪、运行、阻塞、死亡。
新建状态:当用new操作符创建一个线程时,如new Thread(r),线程处于新建状态。在该状态时,程序还没有开始运行线程中的代码。
就绪状态:当创建完线程,调用start()方法后启动线程,当start方法返回后,线程处于就绪状态。处于该状态时,线程不一定会马上去执行run方法,需要同其他线程竞争cpu时间,当获得cpu时间后,才可以运行线程。注:不能重复调用start方法
运行状态:当线程获得cpu时间,进入运行状态,此时开始真正执行run()方法。
阻塞状态:线程运行时,会有各种原因进入阻塞状态。
死亡状态:有两个原因:
查看线程是否存活(要么就是可运行的、要么就是阻塞状态),调用isAlive方法。如果可运行或被阻塞,返回true,如果仍是新建状态或不可运行,返回false。
10.线程管理
线程睡眠sleep()使线程进入阻塞状态。sleep(long mill)是静态方法。他睡眠的始终是当前正在运行的线程。所以如果使用Thread的实例对象去调用它,可能达不到想要的效果。
package com.bin;
class DemoThread extends Thread{
//在run方法中 不能抛出异常 只能try catch处理
@Override
public void run() {
for (int i=0;i<10;i++){
//sleep 毫秒数
try {
//sleep 让当前线程从运行状态变为休眠状态再到运行状态
//sleep 不能是释放锁 多线程之间释放同步 wait可以释放锁
Thread.sleep(1000);
//获取线程的ID ID是多线程随机不重复的主键
System.out.println("getId():"+ getId());
System.out.println("getName():"+ getName());
//如果是实现Runnable接口创建多线程 那么不能直接调用getId getName方法 要先获 //取当前线程对象 System.out.println("Thread.currentThread().getId()+"+Thread.currentThread().getId());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("i:"+i);
}
}
}
public class ThreadDemo05 {
public static void main(String[] args) {
DemoThread demoThread = new DemoThread();
//自定义线程名称
demoThread.setName("线程1");
DemoThread demoThread1 = new DemoThread();
demoThread1.setName("线程1");
demoThread.start();
demoThread1.start();
}
}
线程让步yield(),与sleep()相似,会暂停当前线程,让出cpu资源。
线程合并join(),将几个并行线程合并成一个单线程。
设置线程优先级 setPriority(int newPriority) 1~10 1最低 10最高 默认5 只能反应线程的紧急程度,不能决定线程的执行顺序。
正确结束线程,正常执行完run方法,然后结束掉。通过循环条件判断和标识符去判断。
int i=0;
boolean next=true;
@Override
public void run() {
while (next) {
if(i==10)
next=false;
i++;
System.out.println(i);
}
}