Java单线程和多线程
线程
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。
单线程
单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。单线程就是进程只有一个线程。
多线程
多线程指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务。因为线程只能在单个进程的作用域内活动,所以创建线程比创建进程要廉价得多,同一类线程共享代码和数据空间,每个线程有独立的运行栈,线程切换的开销小。因此多线程编程在现代软件设计中被大量采用。
线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
在Java中要想实现多线程,有两种手段,一种是继承Thread类,另外一种是实现Runable接口。(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用。)
并发性(concurrency)和并行性(parallel)是不同的。并行指的是同一时刻,多个指令在多台处理器上同时运行。并发指的是同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,看起来就好像多个指令同时执行一样。
main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在Java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用Java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实际在就是在操作系统中启动了一个进程。
线程状态
新建的线程在它的一个完整生命周期中通常经历5种状态:新建状态、就绪状态、运行状态、阻塞状态、终止状态。
一个线程通过对象创建方式建立,线程对象通过调用start()方法进入到 “就绪状态” ,一个处于 “就绪状态” 下的线程将有机会等待调度程序安排CPU时间片进入到 “运行状态” 。
在 “运行状态” 的线程根据情况有如下3种可能的走向:
- 时间片执行时间用完它将重新回到 “就绪状态” ,等待新的调度运行机会。
- 线程的run()方法代码执行完毕将进入到 “终止状态”
- 线程可能因某些事件的发生或者等待某个资源而进入到 “阻塞状态”。阻塞条件解除后线程将进入 “就绪状态”
Thread类常用方法
static Thread currentThread() 返回对当前正在执行的线程对象的引用。
long getId()返回该线程的标识符。
String getName()返回该线程的名称。
int getPriority() 返回线程的优先级。
void interrupt() 中断线程。
boolean isAlive()测试线程是否处于活动状态。
void join()等待该线程终止。
void join(long millis)等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos)等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
void setDaemon(boolean on)将该线程标记为守护线程或用户线程。
void setPriority(int newPriority)更改线程的优先级。
static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
static void sleep(long millis, int nanos)在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
static void yield()暂停当前正在执行的线程对象,并执行其他线程。
单线程demo
public class SingleThread {
public static void main(String[] args){
Thread thread = Thread.currentThread(); // 获取当前运行的线程对象
thread.setName("单线程"); // 线程重命名
System.out.println(thread.getName() + "正在运行");
for (int i = 1; i <= 10; i++) {
System.out.println("线程正在休眠:" + i);
try {
Thread.sleep(1000); // 线程休眠,延迟一秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("线程出错");
}
}
}
}
输出结果:
单线程正在运行
线程正在休眠:1
线程正在休眠:2
线程正在休眠:3
线程正在休眠:4
线程正在休眠:5
线程正在休眠:6
线程正在休眠:7
线程正在休眠:8
线程正在休眠:9
线程正在休眠:10
继承Thread类实现多线程demo
public class TestThread {
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1 = new ExtendThread("t1", 1000); // 使用上转对象创建线程,并构造线程名字和线程休眠时间
Thread t2 = new ExtendThread("t2", 1000);
Thread t3 = new ExtendThread("t3", 1000);
t1.start(); // 启动线程并调用run方法
t2.start();
t3.start();
}
}
class ExtendThread extends Thread { // 继承Thread的类
String name;
int time;
public ExtendThread(String name, int time) { // 构造线程名字和休眠时间
this.name = name;
this.time = time;
}
public void run() { // 重写Thread类的run方法
for (int i = 1; i <= 3; i++) {
try {
sleep(time); // 所有线程加入休眠
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("线程中断异常");
}
System.out.println("名称为:" + name + i + ",线程休眠:" + time + "毫秒");
}
}
}
输出结果:
名称为:t11,线程休眠:1000毫秒
名称为:t21,线程休眠:1000毫秒
名称为:t31,线程休眠:1000毫秒
名称为:t22,线程休眠:1000毫秒
名称为:t12,线程休眠:1000毫秒
名称为:t32,线程休眠:1000毫秒
名称为:t13,线程休眠:1000毫秒
名称为:t23,线程休眠:1000毫秒
名称为:t33,线程休眠:1000毫秒
实现Runnable接口的多线程demo
public class RunnableThread {
public static void main(String[] args){
Runnable r1=new ImplRunnable("r1",1000); //Runnable接口必须依托Thread类才能创建线程
Thread t1=new Thread(r1); //Runnable并不能调用start()方法,因为不是线程,所以要用Thread类加入线程
Runnable r2=new ImplRunnable("r2",1000);
Thread t2=new Thread(r2);
Runnable r3=new ImplRunnable("r3",1000);
Thread t3=new Thread(r3);
t1.start(); //启动线程并调用run方法
t2.start();
t3.start();
}
}
class ImplRunnable implements Runnable { // 继承Runnable接口的类
String name;
int time;
public ImplRunnable(String name, int time) { // 构造线程名字和休眠时间
this.name = name;
this.time = time;
}
@Override
public void run() { // 实现Runnable的run方法
for (int i = 1; i <= 3; i++) {
try {
Thread.sleep(time); // 所有线程加入休眠
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("线程中断异常");
}
System.out.println("名称为:" + name + i+",线程休眠:" + time + "毫秒");
}
}
}
输出结果:
名称为:r21,线程休眠:1000毫秒
名称为:r11,线程休眠:1000毫秒
名称为:r31,线程休眠:1000毫秒
名称为:r22,线程休眠:1000毫秒
名称为:r12,线程休眠:1000毫秒
名称为:r32,线程休眠:1000毫秒
名称为:r23,线程休眠:1000毫秒
名称为:r13,线程休眠:1000毫秒
名称为:r33,线程休眠:1000毫秒