目录
1. 进程和线程
进程:进程就是操作系统中运行的每一个应用程序。例如:微信,QQ。
线程:线程是在进程中的每一个任务。
多线程:在一个进程中,可以同时执行多个线程。同时完成多个任务。
并发:同时发生。多个线程同时完成任务。
我们编写的应用程序运行时,也是一个进程。在我们进程中也是有线程的。
第一个线程称为主线程:main方法就是第一个线程。
2. CPU轮转时间片
CPU是中央处理器。程序想要运行必须使用CPU。
一个CPU同一时间只能处理一个程序运行。
一个程序想要运行必须抢到CPU。
程序启动与程序运行不是相同的概念了。
3. Thread类
Thread是线程的父类。
3.1 构造方法
public Thread()
public Thread(String name) //name 是线程的名称
public Thread(Runnable target) //Runnable是任务。
3.2 常用方法
public synchronized void start() :线程启动方法。
线程启动之后,表示当前这个线程对象可以被运行。
public void run() :线程运行方法。
4. 使用Thread类创建自定义的线程类
创建Thread类的子类,重写run的方法。为这个方法编写具体任务代码。
public class Thread1 extends Thread{
@Override
public void run() {
for (int i=1;i<=1000;i++){
System.out.println(i+":zsl");
}
}
}
public class Thread2 extends Thread{
@Override
public void run() {
for (int i=1;i<=1000;i++){
System.err.println(i+":zxb");
}
}
}
public static void main(String[] args) {
Thread thread1 = new Thread1();
thread1.start();
Thread thread2 = new Thread2();
thread2.start();
}
5. 为什么用多线程
抢夺CPU几率增大,执行效率变高。
public class Thread01 extends Thread{
@Override
public void run() {
for(int i = 1;i<=1000;i++) {
System.out.println(i+":abc");
}
}
}
public static void main(String[] args) {
Thread thread = new Thread01();//创建了一个新的子线程。
thread.start();//启动子线程。
for(int i = 1;i<=1000;i++) {
System.out.println(i+":000");
}
}
输出结果:abc 与 000 随机输出。
在main方法中调用了thread.start();就表示在主线程中开辟了一个新的子线程。
主线程与子线程是并列的关系。二个线程之间的执行要通过抢CPU。
6. 使用Runnable接口创建线程任务
使用Runnable接口创建线程任务:任务接口。
@FunctionalInterface:函数式接口
当接口中只有一个抽象方法时,也为函数式接口。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
编写Runnable接口的实现类:任务类。
public class Runnable1 implements Runnable{
@Override
public void run() {
for (int i=1;i<=1000;i++){
System.out.println(i+":$$$");
}
}
}
使用任务类来创建一个线程对象。
将任务类交给线程对象进行加载。线程启动时就可以调用任务类的run方法。
/**
* 主线程,Java启动的方法
* @param args
*/
public static void main(String[] args) {
//使用子类,建一个新的子线程
Thread thread1 = new Thread1();
thread1.start();
Thread thread2 = new Thread2();
thread2.start();
//使用接口实现类
Runnable1 runnable1=new Runnable1();
Thread thread3=new Thread(runnable1);
thread3.start();
}
7. 使用Callable接口编写任务类
在JDK5加入了一些关于线程的新的类。
使用Callable接口编写任务类解决了Runnable接口的二个问题:
① 异常没有抛出。调用线程启动时类,不知道在线程执行过程中有没有出现异常。
② run方法没有返回值。调用线程结束之后。没有数据返回。
编写Callable接口下的实现类。任务类
public class Callable1<S> implements Callable<String> {
@Override
public String call() throws Exception {
for (int i=1;i<=1000;i++){
System.out.println(i+":***");
}
return "任务完成!";
}
}
8. 使用线程池执行Callable接口的任务
8.1 线程池
线程池是存放大量线程对象的容器。
一个线程对象(Thread t)可以加载多个任务(Runnable r,Callable c)
多个任务以串行方式、先后顺序的方式执行。有多个任务可以同时使用同一个线程对象。
线程池:就是一个存放了大量线程对象的容器。
当有任务需要执行时,从线程池中拿一个线程对象。任务执行完成之后。将线程对象还给线
程池。
8.2 线程池Executor接口
实际开发时使用Executor接口的子接口ExecutorService
Submit方法称为注册任务方法。
public interface ExecutorService extends Executor{
<T> Future<T> submit(Callable<T> task);
}
这是一个创建线程池对象的工具类。
通过这个类的静态方法来获得不同的线程池对象。
public class Executors {
返回一个有固定大小的线程池容器对象
public static ExecutorService newFixedThreadPool(int nThreads);
返回只有一个线程对象的线程池对象
public static ExecutorService newSingleThreadExecutor()
返回一个可变化的线程池对象
public static ExecutorService newCachedThreadPool()
}
8.3 返回值Future接口
用来接收Callable任务执行完成之后的返回值。
public interface Future<V> {
获取返回值对象的方式
V get() throws InterruptedException, ExecutionException;
}
public class ExectorTest {
public static void main(String[] args) {
//使用子类,建一个新的子线程
Thread thread1 = new Thread1();
thread1.start();
Thread thread2 = new Thread2();
thread2.start();
//使用runnable接口实现类
Runnable1 runnable1=new Runnable1();
Thread thread3=new Thread(runnable1);
thread3.start();
//使用callable接口实现类
Callable1 callable1 = new Callable1();
ExecutorService service= Executors.newFixedThreadPool(3);
Future<String> future1 = service.submit(callable1);
try {
System.out.println(future1.get());
} catch (Exception e) {
e.printStackTrace();
} finally {
service.shutdown();
}
}
}
9. 线程状态
9.1 初始状态
一个刚被创建好的线程对象,就是初始状态的对象。
Thread t = new Thread(new Runnable1());
当前新创建的 t 线程对象就是初始状态。
9.2 可运行状态
一个线程想要运行必须先抢到CPU。启动与运行是不同的概念。
当一个线程对象调用了start()方法启动了,但还没有抢到CPU时,都是一个可运行状态。
可运行状态的线程对象只做一件事:抢CPU。
9.3 运行状态
一个抢到CPU正在执行的线程对象称为运行状态。
当一个运行状态的线程对象CPU时间到期后要转换为可运行状态。
9.4 终止状态
一个执行完成run()方法的线程对象,就是终止状态。
10. 线程中的常用属性和方法
10.1 name属性
用来表示一个线程对象的名称。可以在创建线程对象时指定。也可以通过方法指定。
public class NameTest {
public static void main(String[] args) {
Thread t = new Thread(new Runnbale1(),"线程1");
//Runnable类见上
System.out.println(t.getName());
}
}
10.2 currentThread()
Thread类的静态方法。动态获取当前执行的线程对象。
public static native Thread currentThread()
public class Runnbale1 implements Runnable{
@Override
public void run() {
for(int i = 1;i<=10;i++) {
String name = Thread.currentThread().getName();
System.out.println(name+"-"+ i+":000");
}
}
}
10.3 sleep()方法
线程运行过程中,遇到sleep方法时线程自己休眠。
public static native void sleep(long millis) throws InterruptedException
public class Runnbale1 implements Runnable{
@Override
public void run() {
for(int i = 1;i<=100;i++) {
String name = Thread.currentThread().getName();
try {
Thread.sleep(1000);//1000为1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"-"+ i+":000");
}
}
}
public class SleepTest {
public static void main(String[] args) {
Thread t1=new Thread(new Runnable1(),"线程1");
t1.start();
}
}
11. 阻塞状态
线程对象运行过程中,出现以下情况:
① 等待IO。做标准输入时,用户需要从控制台输入数据时。
② sleep方法。当前线程休眠。
当线程遇到以上二种情况时,会进入阻塞状态。并会释放CPU。
简化版:
复杂版:
12. 本地线程对象:ThreadLocal<T>类
本地线程对象其实是一个容器。而且是一个只能保存一个数据的容器对象。
这个容器在每一个不同的线程中,都有一份自己的实例。
每一个线程对象都使用自己的ThreadLocal类的实例。
当出现多个线程时,每一个线程中都有一个自己使用的ThreadLocal对象。
12.1 常用方法
public static void main(String[] args) {
ThreadLocal<String> local = new ThreadLocal<>();
local.set("peter");
String s = local.get();
System.out.println(s);
}