一、多线程基础
1.1 进程
进程是操作系统中运行的一个任务(一个应用程序运行在一个进程中)。
进程(process)是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。
1.2 线程
进程中所包含的一个或多个执行单元称为线程(thread)。
线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程。
同类的多个线程共享一块内存空间和一组系统资源,线程本身有一个供程序执行时的堆栈。线程在切换时负荷小,因此,线程也被称为轻负荷进程。一个进程中可以包含多个线程。
1.3 进程与线程的区别
一个进程至少有一个线程。
进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大的提高了程序的运行效率。
线程在执行过程中与进程的区别在于每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,由应用程序提供多个线程执行控制。
1.4 多线程的原理
多个线程“同时”运行,只是我们感官上的一种表现。事实上,线程是并发运行的,OS将时间划分为很多时间片段(时间片),尽可能均匀分配给每一个线程,获取时间片段的线程被CPU运行,而其他线程全部等待。所以微观上是走走停停的,宏观上都在运行。这种现象叫并发,但不是绝对意义上的“同时发生”。
二、创建线程
2.1 继承Thread类方式创建线程
Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。其中重写run方法的目的是定义该线程要执行的逻辑。启动线程时调用线程的start()方法而非直接调用run()方法,start()方法会将当前线程纳入线程调度,使当前线程可以开始并发运行。当线程获取时间片段后会自动开始执行run方法中的逻辑。
package day09;
/**
* 第一种创建线程的方式
* 继承Thread并重写run方法来定义线程要执行的任务。
* @author xxx
*
*/
public class ThreadDemo1 {
public static void main(String[] args) {
Thread t1 = new MyThread1();
Thread t2 = new MyThread2();
/*
* 启动线程要指定start方法,而不是直接调用run方法。run方法是线程要执行的任务。
* 当线程的start方法被调用后,线程进入runnable状态,一旦获取CPU时间片,
* run方法会自动被调用。
*/
t1.start();
t2.start();
}
}
/*
* 第一种创建线程的方式有两个不足:
* 1、由于java是单继承,那么当继承了Thread后就无法再继承其他类;
* 2、由于继承Thread后重写run方法规定了线程执行的任务,这导致线程与任务有一个必然的耦合关系,不利于线程的重用。
*/
class MyThread1 extends Thread{
public void run() {
for (int i = 0;i < 1000;i++) {
System.out.println("你是谁?");
}
}
}
class MyThread2 extends Thread{
public void run() {
for (int i = 0;i < 1000;i++) {
System.out.println("嗨嗨");
}
}
}
2.2 实现Runnable接口方式创建线程
实现Runnable接口并重写run方法来定义线程体,然后在创建线程的时候将Runnable的实例传入并启动线程。
package day09;
/**
* 第二种创建线程的方式
* 实现Runnable接口并重写run方法。
* @author xxx
*
*/
public class ThreadDemo2 {
public static void main(String[] args) {
//单独定义任务
Runnable r1 = new MyRunnable1();
Runnable r2 = new MyRunnable2();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
class MyRunnable1 implements Runnable{
public void run() {
for (int i = 0;i < 1000;i++) {
System.out.println("你是谁?");
}
}
}
class MyRunnable2 implements Runnable{
public void run() {
for (int i = 0;i < 1000;i++) {
System.out.println("嘿嘿");
}
}
}
2.3 使用匿名内部类实现上述两种创建线程的方式
package day09;
/**
* 使用匿名内部类来完成方式一与方式二的线程创建
* @author xxx
*
*/
public class ThreadDemo3 {
public static void main(String[] args) {
//方式一
Thread t1 = new Thread(){
public void run() {
for (int i = 0;i < 1000;i++) {
System.out.println("你是谁?");
}
}
};
t1.start();
//方式二
new Thread(new Runnable() {
public void run() {
for (int i = 0;i < 1000;i++) {
System.out.println("嘿嘿");
}
}
}).start();
}
}
三、线程操作API
3.1 Thread.currentThread()方法
package day09;
/**
* static Thread currentThread()
* 获取运行当前方法的线程
* @author xxx
*
*/
public class ThreadDemo4 {
public static void main(String[] args) {
Thread main = Thread.currentThread();
System.out.println("运行main方法的线程是:"+main);
dosome();
Thread t = new Thread() {
public void run() {
Thread t = Thread.currentThread();
System.out.println("自定义线程:"+t);
dosome();
}
};
t.start();
}
public static void dosome() {
Thread t = Thread.currentThread();
System.out.println("运行dosome方法的线程是:"+t);
}
}
3.2 获取线程信息相关方法
package day09;
/**
* 获取线程相关信息的方法
* @author xxx
*
*/
public class ThreadDemo5 {
public static void main(String[] args) {
//获取运行main方法的线程
Thread main = Thread.currentThread();
long id = main.getId();
System.out.println("id:"+id);
String name = main.getName();
System.out.println("name:"+name);
int priority = main.getPriority();
System.out.println("优先级:"+priority);
boolean isAlive = main.isAlive();
System.out.println("是否存活:"+isAlive);
boolean isDaemon = main.isDaemon();
System.out.println("是否为守护进程:"+isDaemon);
boolean isInterrupted = main.isInterrupted();
System.out.println("是否被中断:"+isInterrupted);
}
}
3.3 setPriority()方法 —— 设置线程优先级
package day09;
/**
* 线程优先级
* 线程的时间片分配完全听线程调度的。线程只能被动的被分配时间,对于线程调度的工作不能干预。
* 但是可以通过提高线程的优先级来达到尽可能干预的目的。
* 理论上,优先级越高的线程,获取CPU时间片的次数就越多。
* @author xxx
*
*/
public class ThreadDemo6 {
public static void main(String[] args) {
Thread min = new Thread() {
public void run() {
for (int i = 0;i < 10000;i++) {
System.out.println(i+":min");
}
}
};
Thread norm = new Thread() {
public void run() {
for (int i = 0;i < 10000;i++) {
System.out.println(i+":nor");
}
}
};
Thread max = new Thread() {
public void run() {
for (int i = 0;i < 10000;i++) {
System.out.println(i+":max");
}
}
};
min.setPriority(Thread.MIN_PRIORITY);
max.setPriority(Thread.MAX_PRIORITY);
min.start();
norm.start();
max.start();
}
}
3.4 sleep()
package day09;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* static void sleep(long ms)
* 线程提供的静态方法sleep可以使运行该方法的线程进入阻塞状态指定毫秒。
* 超时后线程会自动回到RUNNABLE状态。
* @author xxx
*
*/
public class ThreadDemo7 {
public static void main(String[] args) {
/*
* 电子表功能,每秒输出一次当前系统时间:
* 14:39:36
*/
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
while (true) {
System.out.println(sdf.format(new Date()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.5 setDaemon(boolean) —— 设置守护线程
package day09;
/**
* 守护线程,又称为后台线程
* 当一个进程中的所有前台线程都结束时,进程就要结束,若还有后台线程运行,
* 那么后台线程会被强制结束。
* @author xxx
*
*/
public class ThreadDemo8 {
public static void main(String[] args) {
/*
* 前台线程
*/
Thread forward = new Thread() {
public void run() {
for (int i = 0;i < 10;i++) {
System.out.println("forward thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread back = new Thread() {
public void run() {
while(true) {
System.out.println("back thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
//设置为后台进程,并且要在start前调用
back.setDaemon(true);
forward.start();
back.start();
//while(true);
}
}
3.6 join()方法
package day09;
/**
* void join()
* join方法可以使调用该方法的线程进入阻塞状态,直到该方法所属线程完成工作才会解除
* 调用该方法线程的阻塞状态。
* join方法一般用来完成多个线程之间的同步工作问题。
* @author xxx
*
*/
public class ThreadDemo9 {
//表示图片是否下载完毕
public static boolean isFinish = false;
public static void main(String[] args) {
final Thread download = new Thread() {
public void run() {
System.out.println("down:开始下载图片...");
for (int i = 1;i < 100;i++) {
System.out.println("down:"+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("down:图片下载完毕!");
isFinish = true;
}
};
Thread show = new Thread() {
public void run() {
System.out.println("show:开始显示图片...");
/*
* 先等待download把图片下载完毕!
*/
try {
download.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!isFinish) {
throw new RuntimeException("图片没有下载完毕!");
}
System.out.println("show:图片显示完毕!");
}
};
download.start();
show.start();
}
}