Java笔记–多线程
在讲线程之前有必要讨论一下进程的定义:进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。进程实体由程序段, 数据段 PCB(进程控制块)组成。线程又是什么?线程可以看做轻量级进程,线程是进程的执行单元,是进程调度的基本单位
还有一个概念就是并发性(concurrency)和并行性(parallel):并行是指在同一时刻,有多条指令在多个处理器上同时运行;并发指的是在同一时刻只能有一条指令执行,即每个指令以时间片为单位来执行
1,线程的创建与启动
Java使用Thread类代表线程,所有线程对象都是Thread类或其子类的实例
1.1,继承Thread类来创建并启动多线程
public class FirstThread extends Thread{
private int i;
@Override
public void run() {
for(i=0;i<100;i++) {
//返回当前线程的名字
System.out.println(getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++) {
//获取当前线程
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20) {
//创建并启动第一个线程
new FirstThread().start();
//创建并启动第二个线程
new FirstThread().start();
/*FirstThread st1 = new FirstThread();
st1.start();
FirstThread st2 = new FirstThread();
st2.start();*/
}
}
}
}
继承Thread类,重写该类的run()方法,也就是这个线程需要完成的任务,线程对象的start()方法可以用来启动该线程
使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量
1.2,实现Runnable接口创建线程类
public class SecondThread implements Runnable{
private int i;
@Override
public void run() {
for(;i<100;i++) {
//实现Runnable接口时,想获取当前线程只能用Thread.currentThread
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20) {
SecondThread st = new SecondThread();
new Thread(st,"新编程1").start();
new Thread(st,"新编程2").start();
/*Thread t1 = new Thread(st,"新编程1");
t1.start();
Thread t2 = new Thread(st,"新编程2");
t2.start();*/
}
}
}
}
实现Runnable接口,同样的在run()方法中编写需要线程实现的操作,采用线程对象的start()方法启动,
使用实现Runnable接口的方法来创建线程类时,多个线程之间可以共享线程类的实例变量
1.3,实现Callable接口通过FutureTask包装器来创建Thread线程
Callable接口提供了一个call()方法可以作为线程执行体,这个方法具有返回值,还可以声明抛出异常
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThirdThread {
public static void main(String[] args) {
//创建Callable对象
ThirdThread rt = new ThirdThread();
//先使用Lambda表达式创建Callable对象
//使用FutureTask来包装Callable对象
FutureTask task = new FutureTask<>((Callable)()->{
int i=0;
for(;i<100;i++) {
System.out.println(Thread.currentThread().getName()+"循环变量i的值:"+i);
}
return i;
});
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+" 的循环变量i的值:"+i);
if(i==20) {
//实质还是以Callable对象来创建并启动的
new Thread(task,"有返回值的线程").start();
/*Thread t = new Thread(task);
t.start();*/
}
}
try {
System.out.println("子线程的返回值:"+task.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.4,Runnable,Callable接口与Thread类的对比
采用实现Runnable,Callable接口优缺点:
1,接口可以多继承,继承了Runnable接口还能继承其他接口
2,适合多个相同线程来处理同一份资源的情况,
3,缺点是,编程稍微复杂,访问当前线程必须使用Thread.currentThread()
采用继承Thread类优缺点:
1,编写简单,访问当前线程可直接用this
2,缺点是,不能再继承其他类
综上,建议采用实现Runnable接口的方法来创建和启动线程
1.5,synchronized关键字
synchronized关键字用于修饰方法和代码块,以实现同步,当多个线程在执行被synchronized修饰的代码,以排队的方式进行处理。当一个线程调用被修饰的代码时,先判断有没有被上锁,如果上锁就说明有其他线程在调用,必须等待其他线程结束调用后才能执行这段代码,synchronized可以在任意对象以及方法上加锁,加锁的这段代码被称为“互斥区”或者“临界区”
synchronized关键字加到static静态方法是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁
2,线程的生命周期
在线程的生命周期中,要经过新建(new),就绪(Runnable),运行(Running),阻塞(Blocked)和死亡(Dead)5种状态,CPU会在不同的线程之间来回切换,线程会不断的经历运行,阻塞
1,新建状态:使用new关键字创建了一个线程,如Thread t = new MyThread();
2,就绪状态:线程对象调用了start()方法后,线程进入就绪状态,就绪状态就是等待CPU的调度执行
3,运行状态:CPU调用就绪状态的线程,此时线程进入运行状态
4,阻塞状态:处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态
5,死亡状态:线程执行完毕或者异常退出,也可以用stop()方法来结束线程,但是容易造成死锁
这些都是java多线程编程最基础的东西,后面的等我看完《Java多线程编程核心技术》再来写