Java多线程学习
本文介绍了Java多线程编程的基本概念和实现方式,主要包括: 三种线程实现方法:继承Thread类、实现Runnable接口、实现Callable接口 线程状态转换:新建、就绪、运行、阻塞、死亡五个状态及其转换条件 线程调度机制:优先级设置和常用调度方法(sleep、join、yield等) 线程同步与通信:wait/notify机制实现线程协作 重点分析了Thread与Runnable的区别,并
·
目录
- 一、扩展java.lang.Thread类
- 二、实现java.lang.Runnable接口
- 三、Thread和Runnable的区别
- 四、线程状态转换
- 五、线程调度
- 六、常用函数说明
- 七、常见线程名词解释
- 八、线程同步
- 九、线程数据传递
基本概念
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。(进程是资源分配的最小单位)
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
- 多进程:操作系统能同时运行多个任务(程序)
- 多线程:在同一程序中有多个顺序流在执行
在Java中实现多线程有三种方式:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口(与Future、线程池结合使用)
一、扩展java.lang.Thread类
这种方法比较常用,适用于简单的线程需求。
package com.multithread.learning;
/**
* 多线程学习
* @author 林炳文
* @time 2015.3.9
*/
class Thread1 extends Thread{
private String name;
public Thread1(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
Thread1 mTh1 = new Thread1("A");
Thread1 mTh2 = new Thread1("B");
mTh1.start();
mTh2.start();
}
}
输出示例:
A运行 : 0
B运行 : 0
A运行 : 1
A运行 : 2
A运行 : 3
A运行 : 4
B运行 : 1
B运行 : 2
B运行 : 3
B运行 : 4
注意事项:
start()方法调用后线程变为可运行态,具体执行时间由操作系统决定- 多线程程序是乱序执行的
Thread.sleep()方法让出CPU资源给其他线程- 重复调用
start()方法会抛出IllegalThreadStateException
二、实现java.lang.Runnable接口
推荐使用这种方式,更灵活且支持资源共享。
package com.multithread.runnable;
/**
* 多线程学习
* @author 林炳文
* @time 2015.3.9
*/
class Thread2 implements Runnable{
private String name;
public Thread2(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
Thread.sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
new Thread(new Thread2("C")).start();
new Thread(new Thread2("D")).start();
}
}
三、Thread和Runnable的区别
实现Runnable接口的优势:
- 适合多个相同程序代码的线程处理同一资源
- 可以避免Java中的单继承限制
- 增加程序健壮性,代码可以被多个线程共享
- 线程池只能放入实现Runnable或Callable的线程
重要提醒:
- main方法本身也是一个线程
- 所有线程同时启动,执行顺序取决于CPU资源分配
- 每次Java程序运行至少启动2个线程:main线程和垃圾收集线程
四、线程状态转换
详细说明:
- 新建状态(New):新创建线程对象
- 就绪状态(Runnable):调用
start()方法后,等待获取CPU - 运行状态(Running):获取CPU,执行程序代码
- 阻塞状态(Blocked):
- 等待阻塞:执行
wait()方法,释放持有的锁 - 同步阻塞:获取同步锁时被其他线程占用
- 其他阻塞:执行
sleep()、join()或I/O请求
- 等待阻塞:执行
- 死亡状态(Dead):线程执行完毕或异常退出
五、线程调度
1. 调整线程优先级
// 优先级常量
Thread.MAX_PRIORITY // 10
Thread.MIN_PRIORITY // 1
Thread.NORM_PRIORITY // 5
// 设置优先级
thread.setPriority(Thread.MAX_PRIORITY);
2. 线程调度方法
- sleep():线程睡眠,转为阻塞状态
- wait():线程等待,直到其他线程调用notify()
- yield():暂停当前线程,让出执行机会
- join():等待其他线程终止
- notify()/notifyAll():唤醒等待的线程
注意:
suspend()和resume()方法在JDK1.5中已废除,因为有死锁倾向。
六、常用函数说明
① sleep(long millis)
在指定毫秒数内让当前线程休眠。
② join()
等待线程终止。
Thread t = new AThread();
t.start();
t.join(); // 等待t线程结束
使用场景:主线程需要等待子线程执行完成。
③ yield()
暂停当前线程,让出CPU给其他线程。
class ThreadYield extends Thread{
public ThreadYield(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("" + this.getName() + "-----" + i);
if (i == 30) {
this.yield(); // 让出CPU
}
}
}
}
④ setPriority()
更改线程优先级。
⑤ interrupt()
向线程发送中断信号。
⑥ wait()和notify()
必须与synchronized一起使用。
经典示例:三线程交替打印ABC
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();
Thread.sleep(100);
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
sleep()和wait()区别
| 特性 | sleep() | wait() |
|---|---|---|
| 所属类 | Thread | Object |
| 锁释放 | 不释放 | 释放 |
| 使用范围 | 任意位置 | 同步块内 |
| 异常处理 | 必须捕获 | 不需捕获 |
共同点:
- 都可以暂停线程指定时间
- 都可以被
interrupt()打断
七、常见线程名词解释
- 主线程:JVM调用main()产生的线程
- 当前线程:通过
Thread.currentThread()获取的线程 - 后台线程(守护线程):为其他线程提供服务,不阻止JVM退出
- 前台线程:接受后台线程服务的线程
常用线程方法:
sleep():线程睡眠isAlive():判断线程是否存活join():等待线程终止activeCount():活跃线程数currentThread():获取当前线程setDaemon():设置守护线程setName():设置线程名称
八、线程同步
synchronized关键字作用域
-
对象实例内
public synchronized void method() { // 同步方法 } -
类的范围
public synchronized static void staticMethod() { // 同步静态方法 } -
同步代码块
synchronized(this) { // 同步代码块 }
重要原则
- synchronized取得的锁都是对象,不是代码段
- 每个对象只有一个锁
- 同步会带来系统开销,可能造成死锁
- 静态同步方法的锁是Class对象
同步示例
对象锁:
public void methodAAA() {
synchronized(this) { // 对象锁
// 同步代码
}
}
类锁:
public void methodBBB() {
synchronized(Foo.class) { // 类锁
// 同步代码
}
}
特殊实例变量作为锁:
class Foo implements Runnable {
private byte[] lock = new byte[0]; // 特殊的instance变量
public void methodA() {
synchronized(lock) {
// 同步代码
}
}
}
九、线程数据传递
9.1 通过构造方法传递数据
public class MyThread1 extends Thread {
private String name;
public MyThread1(String name) {
this.name = name;
}
public void run() {
System.out.println("hello " + name);
}
public static void main(String[] args) {
Thread thread = new MyThread1("world");
thread.start();
}
}
9.2 通过变量和方法传递数据
public class MyThread2 implements Runnable {
private String name;
public void setName(String name) {
this.name = name;
}
public void run() {
System.out.println("hello " + name);
}
public static void main(String[] args) {
MyThread2 myThread = new MyThread2();
myThread.setName("world");
Thread thread = new Thread(myThread);
thread.start();
}
}
9.3 通过回调函数传递数据
class Data {
public int value = 0;
}
class Work {
public void process(Data data, Integer... numbers) {
for (int n : numbers) {
data.value += n;
}
}
}
public class MyThread3 extends Thread {
private Work work;
public MyThread3(Work work) {
this.work = work;
}
public void run() {
java.util.Random random = new java.util.Random();
Data data = new Data();
int n1 = random.nextInt(1000);
int n2 = random.nextInt(2000);
int n3 = random.nextInt(3000);
work.process(data, n1, n2, n3); // 回调函数
System.out.println(n1 + "+" + n2 + "+" + n3 + "=" + data.value);
}
public static void main(String[] args) {
Thread thread = new MyThread3(new Work());
thread.start();
}
}
更多推荐



所有评论(0)