基本概念
- 进程:进程拥有独立的代码和数据空间,是资源分配的最小单位。一个进程可以包含一个或多个线程。
- 线程:同一类的线程共享代码和数据空间,每个线程都拥有独立的运行栈和程序计数器。线程是cpu调度的最小单位。
- 进程/线程 的五个状态:创建、就绪、运行、阻塞、终止
- 多进程代表操作系统可运行多程序,线程代表程序里运行多个顺序流
创建java线程
java创建线程有三种方法
- 基础Thread类
- 实现Runable接口
- 实现Callable接口,配合Future、线程池
继承Thread类
class threadLearn extends Thread{
public void run(){
//...
}
}
public class Main{
public static void main(){
ThreadLearn learn = new ThreadLearn ();
learn.start();
}
}
实现java.lang.Runnable接口
class ThreadLearn implements Runnable{
@Override
public void run(){
//...
}
}
public class Main{
public static void main(){
ThreadLearn learn = new ThreadLearn ();
learn.start();
}
}
-
start()方法使线程变成可运行状态Runnable,操作系统决定何时调用运行。 -
start()方法重复调用会抛出java.lang.IllegalThreadStateException异常 -
run()方法是多线程程序的一个约定,所有的多线程代码都要写在里面。
Thread和Runnable比较
-
Thread本质上是实现了Runnable接口。 -
Thread类实现了Runnable并在其之上进行了拓展。
线程状态

线程状态转换
| 线程状态 | 状态分类 | 描述 |
|---|---|---|
新建状态New
|
- | 新创建了一个线程对象 |
就绪状态Runnable
|
- | 其它线程调用了该线程的start()方法,线程变为可运行 |
运行状态Running
|
- | 就绪状态的线程被系统调用。 |
阻塞状态Blocked
|
- | 分为三种情况 |
| 阻塞状态 | 等待阻塞 | 运行的线程执行wait()方法 |
| 阻塞状态 | 同步阻塞 | 获取同步锁时锁被其它线程占用,jvm会把该线程放入锁池之中 |
| 阻塞状态 | 其它阻塞 | 线程运行sleep()或join()的线程结束或发出I/O请求。sleep不会释放锁 |
| 死亡状态 | - | 线程执行完毕或异常退出 |
线程调度
- 线程优先级
- Thread的
setPriortity()和getPriortity()管理优先级 - 优先级取值1~10整数,推荐使用常量,这三个级别的可移植性好。
static int MAX_PRIORITY=10;
static int NORM_PRIORITY=5;//默认
static int MIN_PRIORITY=1;
线程睡眠
Thread.sleep(long millis):设定线程阻塞时间,阻塞结束后进入就绪状态。线程等待
Object类中的wait()方法,导致当前线程等待,直到其它线程调用此对象的notify()方法或者notifyAll()唤醒,等价于wait(0)线程让步
Thread.yield()暂停当前正在执行的线程对象,把执行的机会让给优先级相同或更高的线程。线程加入
join()当前线程进入阻塞态,调用其它线程,该线程运行完毕后当前线程再进入就绪态。线程唤醒
Object类的notify()方法,唤醒对象监听器上一个线程,如果有多个线程在此对象上等待,则唤醒随机一个。notifyAll()唤醒所有该对象上等待的线程。
wait()和sleep()都可以通过interrupt()方法 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。
sleep方法不会释放锁,wait会
wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
常用方法
-
sleep(): 强迫一个线程睡眠N毫秒。 -
isAlive(): 判断一个线程是否存活。 -
join(): 等待线程终止。 -
activeCount(): 程序中活跃的线程数。 -
enumerate(): 枚举程序中的线程。 -
currentThread(): 得到当前线程。 -
isDaemon(): 一个线程是否为守护线程。 -
setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于没有用户线程后守护线程终止) -
setName(): 为线程设置一个名称。 -
wait(): 强迫一个线程等待。 -
notify(): 通知一个线程继续运行。 -
setPriority(): 设置一个线程的优先级。
线程同步
synchronized[ˈsɪŋkrənaɪzd]是系统级的锁,一旦锁死除了线程自行释放没有其它方法。juc的lock锁是编码级别的,可以代码解锁。juc(java.util.concurrent)下次讨论
-
synchronized在对象里:标记于方法或是代码块都是对对象加锁。只要对象中出发了锁,整个对象都无法进入。 -
synchronized标记于静态方法,则是对于类加锁,和对象锁不冲突
线程数据传递
同步情况下使用参数传入,return返回的形式,多线程下运行和结束是不可预料的,所以无法和同步一样传参。
- 使用构造方法传参
//线程使用构造函数就收这个参数
Thread thread = new MyThread1("hello world");
-
set方法
线程里先设置set方法接受参数(不用多说了吧)
然后start()之前设置参数
MyThread2 myThread = new MyThread2();
myThread.setName("hello world");
Thread thread = new Thread(myThread);
thread.start();
- 回调函数
将对象传入线程,线程在某一时间调用对象的函数。主线程通过传入的对象获取线程操作后的值。(还有静态类)
- 声明lambda函数的接口
public interface ICallback {
public void callback(Map<String, Object> params);
}
- 线程调用接口返回数据
public static void doStm(final ICallback callback) {
// 初始化一个线程
Thread t = new Thread() {
public void run() {
// 这里是业务逻辑处理
System.out.println("子线任务执行:"+Thread.currentThread().getId());
// 为了能看出效果 ,让当前线程阻塞5秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 处理完业务逻辑,
Map<String, Object> params = new HashMap<String, Object>();
params.put("a1", "这是我返回的参数字符串...");
callback.callback(params);
};
};
es.execute(t);
//一定要调用这个方法,不然executorService.isTerminated()永远不为true
es.shutdown();
}
doStm((params)->{
System.out.println("单个线程也已经处理完毕了,返回参数a1=" + params.get("a1"));
});