Thread
- 可以实现多部分程序的同时执行
- 多线程可以合理使用CPU的资源,然而如果线程过多将会导致性能降低
- 每条线程都有自己的名称,主方法的线程名为main,新建线程的默认名从Thread-0开始依次递增
- 默认创建的线程为用户线程
线程的使用方法
方法1
- 定义一个类并继承Thread类
- 重写run方法
- 调用start方法开启线程
class Demo extends Thread{
@Override
public void run(){
// 任务
}
}
Demo d1 = new Demo();
// 启动线程
d2.start();
方法2(推荐)
- 定义一个类并实现Runnable接口
- 重写run方法
- 创建Thread类对象
- 将Runnable接口的子类对象作为参数传递给Thread类的构造方法
- 调用Thread类的start方法开启线程
class Demo2 implements Runnable{
@Override
public void run() {
// 任务
}
}
Demo2 d = new Demo2();
// 创建Thread类对象,将Runnable接口的子类对象作为参数传递给thread类的构造方法
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
// 启动线程
t1.start();
t2.start();
线程的运行安全

同步代码块
- 使用一个对象作为锁
一个栗子
Object obj = new Object();
public void run(){
while(true){
synchronized(obj){
//需要同步的代码
}
}
}
同步方法
- 同步方法的锁对象是this
- 静态同步方法的锁对象是类名.class
一个栗子
public synchronized void sale(){
//需要同步的代码
}
线程常用方法
sleep()
强迫一个线程睡眠N毫秒
join()
等待线程终止
currentThread()
得到当前线程(拿到当前方法所在的线程对象)
isDaemon()
一个线程是否为守护线程
setDaemon()
设置一个线程为守护线程
setName()
为线程设置一个名称
wait()
强迫一个线程等待
notify()
通知一个线程继续运行
setPriority()
设置一个线程的优先级
单例模式线程安全
多线程操作单例模式(延迟加载方式)时所发生的问题:
- 并发访问会有安全隐患,所以加入同步机制解决安全问题,但是同步的出现降低了效率
- 通过双重判断的方式,解决效率问题,减少判断锁的次数
栗子1 -- 无线程安全问题
class Single {
private Single() {}
private static final Single s = new Single();
public static Single getInstance() {
return s;
}
}
栗子2 -- 延迟加载方式线程安全问题解决方案
class Single2 {
private Single2() {}
private static final Single s2 = null;
public static Single getInstance() {
if (s2 == null) {
synchronized (Single2.class) {
if (s2 == null) {
s2 = new Single2();
}
}
}
return s2;
}
}
接口Lock与监视器对象(Condition)
- 接口Lock将锁定义为对象,所以锁的监视器方法(wait,notify,notifyAll)也替换成新锁的监视器方法
- 将原有的锁方法封装到了Condition对象中,想要获取监视器方法,要先获取Condition对象
- Condition对象的出现,替代了Object中的监视器方法
监视器方法:
await()
线程进入等待
signal()
随机唤醒一个等待线程
signalAll()
唤醒所有等待线程
Lock与Condition代码实例 -- 生产者与消费者问题
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
// 定义一个锁对象
private Lock lock = new ReentrantLock();
// 获取锁上的Condition对象
// 为了解决本方唤醒对方问题, 可以一个锁创建两个监视器对象
private Condition producer = lock.newCondition();// 生产
private Condition consumer = lock.newCondition();// 消费
public void set(String name) {
// 获取锁
lock.lock();
try {
// 随机抽取线程进入锁, while判断标记为false则执行生产代码, 如果为true则等待
while (flag) {
try {
producer.await();
} catch (InterruptedException e) {
e.printStackTrance();
}
this.name = name + count;
count ++;
System.out.println(Thread.currentThread().getName() + "..生产者.." + this.name);// 生产商品
flag = true;
// 执行消费线程的唤醒, 随机唤醒一个消费线程释放锁
consumer.signal();
}
} finally {
lock.unlock;
}
}
public void out() {
lock.lock();
try {
// 随机抽取线程进入锁, while判断标记为false则执行生产代码, 如果为true则等待
while (!flag) {
try {
consumer.await();
} catch (InterruptedException e) {
e.printStackTrance();
}
System.out.println(Thread.currentThread().getName() + "..消费者.." + this.name);// 消费商品
// 标记修改为false
flag = false;
// 随机唤醒一个生产线程释放锁
producer.signal();
}
} finally {
lock.unlock;
}
}
}
sleep()与wait()的区别
相同点:
- 可以让线程除于冻结状态
不同点:
- sleep必须指定时间,wait可以指定也可以不指定时间
- sleep时间一到,线程处于临时阻塞状态或运行状态,wait如果没有指定的时间,必须要通过notify或者notifyAll唤醒
- sleep不一定非要定义在同步中,wait必须要定义在同步中
- 都定义在同步中时,线程执行到sleep不会释放锁,而线程执行到wait会释放锁
线程的细节
停止线程
- stop方法已过时,不推荐使用
- 当线程任务执行结束时(run方法结束),线程结束
- 使用interrupt方法强制中断线程
守护线程
- 守护线程为后台线程,后台线程会随着前台线程的结束而结束
- 在启动线程前使用
setDaemon(true)
将线程设置为守护线程
线程的优先级
类型 | 字段 | 注释 |
---|---|---|
static int | MAX_PRIORITY | 线程可以具有的最高优先级----优先级范围(10) |
static int | MIN_PRIORITY | 线程可以具有的最低优先级----优先级范围(5) |
static int | NORM_PRIORITY | 分配给线程的默认优先级----优先级范围(0) |
等待线程终止
join()
等待该线程终止
-
t.join()
方法阻塞调用此方法的线程(calling thread),直到线程t完成,此线程再继续;通常用于在main()主线程内,等待其它线程完成再结束main()主线程
线程临时暂停
Thread.yield()
临时停止当前正在运行的线程,让另外的线程执行
匿名线程
使用匿名内部类创建匿名线程以及实现Runnable接口
一个栗子
// 匿名内部类
new Thread() {
@Override
public void run() {
// 任务
}
}.start();
// 实现Runable接口创建线程
Runnable r = new Runnable() {
@Override
public void run() {
// 任务
}
};
new Thread(r).start();
网友评论