Java多线程笔记
线程的简介
- 程序:指令和数据的有序集合,是一个静态的概念。
- 进程:执行程序的一次执行过程,是一个动态的概念,是系统资源的分配单位。
- 线程:一个进程可以包含多个线程, 线程是CPU的调度和执行单位。
由main
开始的线程是用户线程, 由GC
使用的线程是守护线程。
创建线程
- 继承Thread类(不赘述)
- 实现Runnable接口(不赘述)
- 实现Callbale接口 (现阶段了解即可)。
静态代理(这里解释Thread 和 Runnable的关系)
Thread
继承了Runnable接口,同时也可以自己实现一个Runnable
, 将其传递给Thread
, 让Thread进行代理执行。
线程的状态:
线程提供的方法:
方法 | 说明 |
---|---|
setProority(int p) | 设置线程的有限级别 |
static void sleep(long milis) | 休眠毫秒 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程,不推荐使用 |
boolean isAlive() | 测试线程是否处于活动状态 |
线程停止/休眠/礼让/强制执行:
- 线程停止:
Thread
类提供了stop
和 destroy
方法,但是已经不推荐使用了,此时可以使用设置一个属性标记线程是否停止,run
方法当中的代码根据此标记来执行即可。
- 线程休眠:
注意,如果在同步代码块儿中使用休眠,会进入阻塞状态,并且设置TIME_WAITING
, 但是其不会释放锁。
- 线程礼让:
yield()
方法是直接让线程进入就绪态,继续争抢CPU
资源。
- 强制执行:
在线程A
中调用B
线程的join()
方法,则线程A
会等待线程B
执行完毕后才会的继续执行下面的代码。
线程的优先级:
范围从1~10,线程优先级高不一定先执行,但是其权重就大了。线程优先级低只是意味着获得调度的概率低,并不是低优先级就不会先执行。
- 三个常量
MIN_PRIORITY = 1; // 最小
MIN_PRIORITY = 5; // 正常值
MIN_PRIORITY = 10; // 最大
守护线程
虚拟机不会管守护线程是否执行完毕,当所有用户线程执行完毕,守护线程也就完毕了。
应用案例如后台的操作日志,监控内容,垃圾回收等内容。
使用setDaemon
为true
即可将其设置为守护线程。
线程的同步
- 并发:
同一个对象被多个线程同时操作。
-
锁存在的问题:
- 加锁,释放锁会导致比较多的上下文切换 和 调度延时问题,引起性能问题。
- 如果一个优先级高的线程等待一个优先级较低的线程释放所,会导致优先级倒置,引起性能问题。
-
同步方法和同步代码块儿:
实现机制是锁 + 队列
。
- 同步方法会严重影响性能,将没有必要锁的位置也给锁上。
public synchronized void method() {}
synchronized (obj) {}
锁
JDK5.0
显式定义同步锁对象来实现同步。RenntrantLock
(可重入锁)类实现了java.util.concurrent.locks.Lock
接口, 拥有和synchronized
相同的并发性和的内存语义, 在实现线程安全控制中,比较常用的是ReentrantLock
, 可以显式的加锁和释放锁。
lock
相比较于synchronized
,JVM会话费更少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)。
class A {
private final ReentrantLock lock = new ReenTrantLick();
public void m() {
lock.lock();
try {
// 保证线程安全的代码
} finally {
lock.unlock();
}
}
}
线程池
避免线程频繁创建,复用线程对象,使用线程池提高性能。
- 相关
API:
ExecutorService
接口与 Executors
。Excetors
为创建 ExecutorService
的工厂类:
package com.company.concurency;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class RunImpl implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程运行完毕");
}
}
public class Pool {
public static void main(String[] args) {
// 参数1, 指定线程池的大小。
ExecutorService executorService = Executors.newFixedThreadPool(2);
// submit 还有个使用Callable接口的重载方法,可以用来接收返回值。
executorService.submit(new RunImpl());
executorService.submit(new RunImpl());
executorService.submit(new RunImpl());
executorService.shutdown();
}
}