什么是线程
首先要了解计算机中的程序、进程、线程分别是什么
- 程序:计算机指令的集合;
- 进程:计算机以“进程”的方式运行程序,并分配“CPU”、“网络”、“磁盘”、“内存”等资源;
- 线程:一个进程中会包含多个进程
线程的创建和实现
线程的创建
创建Thread对象就是创建一个线程(创建Thread对象的两种方法)
方法一:通过new一个Runnable接口的实现类
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
for(char c='a';c<'z';c++) {
System.out.println(Thread.currentThread().getName()+":"+c);
}
}
},"子线程1");
需要注意的是Runnable接口实现类的run方法没有返回值
方法二:通过创建Thread类的子类
Thread thread2 = new Thread("子线程2") {
@Override
public void run() {
for(int c=1;c<26;c++) {
System.out.println(Thread.currentThread().getName()+":"+c);
}
}
};
线程的实现
1.通过实现Runnable接口(实现Runnable接口,无返回值)
2.继承Thread类
3.实现Callable接口(子线程执行后,有返回值)
- 先创建Callable接口的实现类
- 再创建Callable接口实现类的对象
- 再将Callable接口实现类的对象转换为FutureTask(相当于是runnable接口实现类,因为Thread对象的有参构造方法只要Runnable)
这里举例说明(使用多线程的方式实现1-10000的和)
package Thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test03 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//使用多线程的方式实现1-10000的和
//创建callable对象
SubTask subTask1 = new SubTask(1, 2000);
SubTask subTask2 = new SubTask(2000, 4000);
SubTask subTask3 = new SubTask(4000, 6000);
SubTask subTask4 = new SubTask(6000, 8000);
SubTask subTask5 = new SubTask(8000, 10001);
//将callnable对象转换为FutureTask(相当于是runnable接口实现类)
FutureTask<Integer> futureTask1 = new FutureTask<Integer>(subTask1);
FutureTask<Integer> futureTask2 = new FutureTask<Integer>(subTask2);
FutureTask<Integer> futureTask3 = new FutureTask<Integer>(subTask3);
FutureTask<Integer> futureTask4 = new FutureTask<Integer>(subTask4);
FutureTask<Integer> futureTask5 = new FutureTask<Integer>(subTask5);
//创建thread对象
Thread thread1 = new Thread(futureTask1);
Thread thread2 = new Thread(futureTask2);
Thread thread3 = new Thread(futureTask3);
Thread thread4 = new Thread(futureTask4);
Thread thread5 = new Thread(futureTask5);
//启动线程
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
//拿到结果
Integer sum1 = futureTask1.get();
Integer sum2 = futureTask2.get();
Integer sum3 = futureTask3.get();
Integer sum4 = futureTask4.get();
Integer sum5 = futureTask5.get();
int total = sum1+sum2+sum3+sum4+sum5;
System.out.println(total);
}
}
class SubTask implements Callable<Integer>{
private int begin,end;
public SubTask(int begin,int end) {
this.begin = begin;
this.end = end;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i= begin;i<end;i++) {
sum += i;
}
return sum;
}
}
4.线程池(提高线程的使用效率)
package Thread;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test04 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//ExecutorService:线程池
//Executors:工具类,用于创建各种形式的线程池
//Executors.newFixedThreadPool(5): 创建固定线程数目的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
int total = 0;
for(int i=1;i<=10000;i+=2000) {
Future<Integer> submit = executorService.submit(new SubTask(i,i+2000));
Integer sum = submit.get();
total +=sum;
}
System.out.println(total);
}
}
线程的方法
获取当前线程
获取当前线程 —— Thread.currentThread() —— 通过方法可以获取当前线程的信息
线程命名
线程的命名有两种方法
-
在创建Thread对象时直接命名
package Thread; public class Demo01 { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } },"子线程一"); } }
-
在创建完线程后通过setName()方法命名
package Thread; public class Demo01 { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }); thread.setName("子线程一"); } }
线程休眠
线程休眠是在代码运行开始时进行休眠,当休眠时间结束时线程自动恢复到RUNNABLE运行状态
线程休眠(参数单位是毫秒) —— Thread.sleep(1000)
线程优先级
当设置线程优先级方法后,线程会优先执行(只是参考,不会完全优先执行)
设置优先级的方法(优先级最高为10、最低为1,默认为5) —— 线程对象.setPriority(1)
package Thread;
public class Test05 {
public static void main(String[] args) {
MyTask myTask = new MyTask();
Thread thread1 = new Thread(myTask);
Thread thread2 = new Thread(myTask);
//设置优先级
thread1.setPriority(1);
thread2.setPriority(10);
thread1.setName("线程一");
thread2.setName("线程二");
thread1.start();
thread2.start();
}
}
class MyTask implements Runnable{
@Override
public void run() {
char c = '王';
for(int i = 0;i<=26;i++,c++) {
System.out.println(Thread.currentThread().getName()+":"+c);
}
}
}
线程的插队
因为JVM虚拟机是争抢机制,所以线程没有顺序可言,但是线程的插队会使执行插队方法的线程对象优先执行
线程插队 —— 线程对象.join();join()方法通过wait()实现,是被插队的线程(主线程),处于等待状态。
package Thread;
public class Test06 {
public static void main(String[] args) {
MyTask myTask = new MyTask();
Thread thread = new Thread(myTask);
thread.start();
try {
thread.join(); //子线程插队
} catch (InterruptedException e) {
e.printStackTrace();
}
//等子线程执行后,主线程恢复执行
System.out.println("main,主线程结束");
}
}
线程的中断
线程中断方法会使该线程中断允许,通过 线程对象.interrupt() 方法的执行来设置当前线程为中断状态,当线程中断时,会抛出InterruptedException异常,同时可以通过isInterrupted()方法来判断当前线程是否为状态。
package Thread;
public class Test07 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程开始执行");
// try {
// Thread.sleep(6000); //子线程休眠
// } catch (InterruptedException e) {
// System.out.println("子线程中断");
// }
//isInterrupted()检查当前线程是否中断
while(!Thread.currentThread().isInterrupted()) {
System.out.println("999");
}
System.out.println("子线程结束");
}
});
thread.start();
try {
Thread.sleep(3000);//主线程休眠
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
thread.interrupt(); // 线程中断
System.out.println("主线程结束");
}
}
线程的让出
线程调度模型:
- 分时调度模型:所有线程轮流获取CPU使用权,平均分配每个线程占用的CPU时间片
- 抢占调度模型:优先分配给优先级较高的线程占用CPU,如果优先级相同,则随机选择一个线程;JVM虚拟机采用抢占调度模型;
线程的让出 (线程的让出也不是绝对的) —— Thread.yield()
package Thread;
public class Test08 {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (char c = 'A'; c < 'Z'; c++) {
Thread.yield(); //让出线程CPU处理器的执行
System.out.println(Thread.currentThread().getName()+":"+c);
}
}
},"线程1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (char c = 'a'; c < 'z'; c++) {
System.out.println(Thread.currentThread().getName()+":"+c);
}
}
},"线程2");
thread1.start();
thread2.start();
}
}
线程的分类
- 用户线程:所有用户线程执行结束后,JVM虚拟机会自动退出;
- 守护线程:守护线程执行结束后,JVM虚拟机不会自动退出;使用setDaemon(true)方法
线程的状态
- NEW(新建状态):调用构造方法后,线程处于新建状态;
- RUNNABLE(运行状态):调用线程的start()方法后,线程处于可运行状态;
- BLOCKED(阻塞状态):两个线程同时竞争synchronized锁时,获取锁的线程处于RUNNABLE运行状态,未获取锁的线程处于BLOCKED阻塞状态;
- WAITING(等待状态):必须通过notify()或notifyAll()
- TIMED_WAITING(计时等待状态):等待时间结束后,自动唤醒
- TERMINATED(终止状态)
户线程执行结束后,JVM虚拟机会自动退出;
- 守护线程:守护线程执行结束后,JVM虚拟机不会自动退出;使用setDaemon(true)方法
线程的状态
- NEW(新建状态):调用构造方法后,线程处于新建状态;
- RUNNABLE(运行状态):调用线程的start()方法后,线程处于可运行状态;
- BLOCKED(阻塞状态):两个线程同时竞争synchronized锁时,获取锁的线程处于RUNNABLE运行状态,未获取锁的线程处于BLOCKED阻塞状态;
- WAITING(等待状态):必须通过notify()或notifyAll()
- TIMED_WAITING(计时等待状态):等待时间结束后,自动唤醒
- TERMINATED(终止状态)