java多线程编程
线程的生命周期
-
新建状态: 使用new关键字创建了一个线程之后,该线程就处于新建状态
-
就绪状态: 调用了start()方法之后,该线程就进入就绪状态。就绪线程处于就绪队列中,等待JVM里线程调度器的调度。
-
运行状态: 线程获取 CPU 资源,就可以执行 run(),此时处于运行状态。可变为阻塞状态、就绪状态和死亡状态。
-
阻塞状态: 程因为某种原因放弃CPU使用权,暂时停止运行,知道进入就绪状态
① 等待阻塞:wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
② 同步阻塞:线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
③ 其他阻塞:sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(sleep是不会释放持有的锁)
-
死亡状态: 运行状态的线程完成任务或者其他终止条件发生时
线程创建
-
实现 Runnable 接口
-
继承 Thread 类本身
-
通过 Callable 和 Future 创建线程
实现 Runnable 接口
public class RunnableTest implements Runnable {
@Override
public void run() {
try {
for (int num = 0; num < 10; num++) {
System.out.println(Thread.currentThread().getName() + ":" + num);
Thread.sleep(50);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
RunnableTest runnableTest = new RunnableTest();
Thread thread1 = new Thread(runnableTest, "thread1");
Thread thread2 = new Thread(runnableTest, "thread2");
thread1.start();
thread2.start();
}
}
继承 Thread 类
主要方法
方法 | 描述 |
---|---|
public final void setPriority(int priority) | 更改线程的优先级(1-10,10优先级最高) |
public final void join(long millisec) | 等待该线程终止的时间最长为 millis 毫秒 |
public void interrupt() | 中断线程 |
public static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
public static void sleep(long millisec) | 线程休眠 |
public static Thread currentThread() | 返回对当前线程对象的引用 |
继承 Thread 类实现
public class ThreadTest extends Thread {
private String name = "";
private Thread thread;
public ThreadTest(String name) {
this.name = name;
}
public synchronized void start() {
if (thread == null) {
thread = new Thread(this, name);
thread.start();
}
}
public void run() {
try {
for (int num = 0; num < 10; num++) {
System.out.println(Thread.currentThread().getName() + ":" + num);
Thread.sleep(50);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ThreadTest threadTest1 = new ThreadTest("thread1");
ThreadTest threadTest2 = new ThreadTest("thread2");
threadTest1.start();
threadTest2.start();
}
}
通过 Callable 和 Future 创建线程
-
创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,有返回值。
-
创建 Callable 实例,使用 FutureTask 类来包装 Callable,FutureTask 封装了 Callable 的 call() 方法的返回值。
-
使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
-
调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值
public class CallableTest implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int num = 0;
for (; num < 100; num++) {
System.out.println(Thread.currentThread().getName() + " num:" + num);
}
return num;
}
public static void main(String[] args) {
CallableTest callableTest = new CallableTest();
FutureTask<Integer> ft = new FutureTask<Integer>(callableTest);
new Thread(ft, "Thread").start();
try {
System.out.println("返回值:" + ft.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
线程调度
优先级
- setPriority(int priority) 和 getPriority()
- static int MAX_PRIORITY 最高10
- static int MIN_PRIORITY 最低1
- static int NORM_PRIORITY 默认5
优先级
线程睡眠
- Thread.sleep(long millis),睡眠指定时间,转到阻塞状态
优先级
线程等待
- Object类中的wait() 进入线程等待池等待, notify() 方法或 notifyAll() 唤醒
优先级
线程让步
- Thread.yield() 暂停线程,让给优先级相同或者更高的线程
优先级
线程加入
- Thread.join() 当前线程中调用另一个线程的join()方法,当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
优先级
线程唤醒 Object类中的notify(),notifyAll() 从等待池中唤醒线程,进入就绪状态
sleep和wait的区别
- sleep来自Thread类,wait来自Object类
- sleep方法没有释放锁,wait方法释放了锁,进入线程等待池等待,调用notify/notifyAll唤醒等待池中的线程
- sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
- wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
线程同步
synchronized
- 同步方法
Public synchronized void method(){
//…..
}
等价于
public void method(){
synchronized (this){
//…..
}
}
- 同步块
对象锁
public void method(Object o) {
synchronized (o) {
//…..
}
}
或当没有明确的对象作为锁,想让一段代码同步,可以创建一个特殊的instance变量(它得是一个对象)来充当锁
private byte[] lock = new byte[0]; // 特殊的instance变量
public void method() {
synchronized (lock) {
//…
}
}
- synchronized作用于static 函数
class Foo {
public synchronized static void methodAAA() {
//….
}
public void methodBBB() {
synchronized (Foo.class) {
}
}
}
两个方法效果是一样的,A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class
重入锁
- 在JavaSE5.0中新增了一个java.util.concurrent包来支持同步
- ReentrantLock类是可重入、互斥、实现了Lock接口的锁
private int num = 100;
private ReentrantLock lock = new ReentrantLock();
//同步方法
public void add(int a) {
lock.lock();
try {
num += a;
} finally {
lock.unlock();
}
}
局部变量ThreadLocal
- ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,相互独立,线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
//使用ThreadLocal类管理共享变量num
private static ThreadLocal<Integer> num = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 100;
}
};
public void add(int a) {
num.set(num.get() + a);
}
其他方法。。。。。。。。。。。。。。。。。。。。。。。。。。
线程死锁
原因
- 系统资源不足
- 进程(线程)推进的顺序不恰当
- 资源分配不当
产生死锁的必要条件
- 互斥条件:所谓互斥就是进程在某一时间内独占资源。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。