1 Thread中的常用方法
1.1 线程休眠方法—Sleep
在Java中,Thread.sleep()
方法用于使当前线程暂停执行指定的毫秒数。这是一个静态方法,它可以让当前执行的线程暂停一段时间,这段时间内该线程不会执行任何代码,也不会消耗CPU资源。
使用Thread.sleep()
时需要注意几点:
sleep()
方法可能会抛出InterruptedException
sleep()
方法不会释放任何锁资源,如果线程在持有某个对象的监视器锁(即,该线程在某个synchronized
块或方法中)时调用了sleep()
方法,那么其他试图进入该synchronized
块或方法的线程将会被阻塞,直到第一个线程醒来并退出synchronized
块或方法。sleep()
只是一个简单的暂停,当线程醒来后,它将从暂停的地方继续执行。
下面是一个简单的示例代码,展示了如何使用Thread.sleep()
方法:
public class SleepExample {
public static void main(String[] args) {
try {
System.out.println("线程开始休眠...");
// 使当前线程休眠3秒钟
Thread.sleep(3000);
System.out.println("线程休眠结束!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这个例子中,主线程会打印一条消息,然后休眠3秒钟,之后再打印另一条消息。
1.2 yield 当前线程让出cpu-参与下次的竞争
当然,下面是一个简单的代码示例,用于解释Thread.yield()
方法的使用及其潜在效果:
public class YieldExample {
public static void main(String[] args) {
// 创建两个线程
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("线程1正在运行: " + i);
Thread.yield(); // 线程1让出CPU执行权
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("线程2正在运行: " + i);
// 线程2没有调用yield(),因此会持续运行直到时间片用完或被更高优先级的线程抢占
}
});
// 启动两个线程
thread1.start();
thread2.start();
}
}
在这个例子中,我们创建了两个线程thread1
和thread2
。thread1
在每次循环中都会调用Thread.yield()
方法,表示它愿意在每次打印之后让出CPU执行权给其他线程。而thread2
则不会主动让出CPU执行权。
1.3 join加入当前线程
当在A线程中调用了B线程的`join()`方法时,A线程会等待B线程执行完毕后再继续执行。如果`join()`方法中传入了参数,如`join(10)`,则表示A线程会等待B线程执行10毫秒,超时后A、B线程将并行执行。值得注意的是,`join(0)`并不意味着A线程等待B线程0秒,而是表示A线程会无限期地等待B线程执行完毕。
1.4 setDaemon()设置线程为守护线程
守护线程是一种在后台运行的线程,其目的是为其他线程(通常是用户线程)提供服务。当程序中没有用户线程在运行时,所有的守护线程都会被自动终止,这意味着如果只剩下守护线程,那么Java虚拟机就会退出。
在Java中,setDaemon(boolean on)
方法用于将一个线程设置为守护线程(daemon thread),这个方法必须在调用线程的
start() 方法之前
被调用。
2 线程安全问题
2.1 什么情况下会出现线程安全问题
当多个线程操作同一个资源时,则出现线程安全问题。
2.2 java如何解决线程安全问题
synchronized自动锁
-
synchronized可以用在方法和代码块上。当修饰方法时,称为同步方法;修饰代码块时,称为同步块。
-
优点:是Java内置的关键字,使用方便,系统会自动释放锁,无需手动管理。
-
缺点:不够灵活,无法判断锁的状态,且在竞争激烈的情况下性能可能不如Lock。
同步方法:直接在方法声明上添加synchronized关键字。
public synchronized void synchronizedMethod() { // 同步代码块 }
同步块:在需要同步的代码块前使用synchronized关键字,并指定一个对象作为锁。
public void someMethod() { synchronized (this) { // 同步代码块 } }
Lock手动锁
创建Lock对象:通常使用ReentrantLock
类的实例。
Lock lock = new ReentrantLock();
获取锁:在需要同步的代码块前调用lock()
方法获取锁。
lock.lock(); // 获取锁
try {
// 同步代码块
} finally {
lock.unlock(); // 释放锁
}
释放锁:在finally块中调用unlock()
方法释放锁,以确保锁一定会被释放。
Lock
接口还提供了其他功能,如:
- 尝试获取锁:
tryLock()
方法尝试获取锁,如果获取成功则返回true,否则返回false。
if (lock.tryLock()) {
try {
// 同步代码块
} finally {
lock.unlock();
}
}
- 超时获取锁:
tryLock(long timeout, TimeUnit unit)
方法尝试在给定的时间内获取锁。
if (lock.tryLock(10, TimeUnit.SECONDS)) {
try {
// 同步代码块
} finally {
lock.unlock();
}
}
3 死锁
线程A拥有锁资源a,希望获取锁资源b,线程B拥有锁资源b,希望获取锁资源a。 两个线程互相拥有对方希望获取的锁资源。可能会出现程序堵塞。从而造成死锁。
解决:
1. 不要使用锁嵌套。
2. 设置超时时间。--Lock类中tryLock.
3. 使用安全java.util.concurrent下的类。
4 线程通信
wait()
方法使当前线程进入等待状态,并且释放当前线程持有的锁,这样其他线程就可以获得该锁并执行相应操作。
notify()
方法用于唤醒等待在当前对象上的一个线程(随机选择),而notifyAll()
方法则用于唤醒所有等待在当前对象上的线程。上述方法
必须在同步方法或同步块中调用
举例:有一张公共银行卡,确保在线程A存钱之后,线程B在取钱。
银行卡
package com.ykq.demo06;
public class BankCard {
private double balance;
private boolean flag;//true:有钱 false:没钱
public synchronized void cun(double money){
if(flag==true){
try {
wait();//属于Object类中。 进入等待队列 并且释放拥有的锁
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//+余额
balance+=money;
//设置有钱标志
flag=true;
//唤醒--唤醒等待队列中的某个线程
notify();
System.out.println(Thread.currentThread().getName()+"往卡中存入了:"+money+";卡中余额:"+balance);
}
public synchronized void qu(double money){
if(flag==false){
try {
wait(); //放入等待队列中。
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
balance-=money;
flag=false;
notify();
System.out.println(Thread.currentThread().getName()+"从卡中取出了:"+money+";卡中余额:"+balance);
}
}
线程A存钱:
public class CunThread extends Thread{
private BankCard bankCard;
public CunThread(BankCard bankCard){
this.bankCard=bankCard;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bankCard.cun(1000);
}
}
}
线程B取钱:
public class QuThread extends Thread{
private BankCard bankCard;
public QuThread(BankCard bankCard){
this.bankCard=bankCard;
}
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
bankCard.qu(1000);
}
}
}
测试:
public static void main(String[] args) {
BankCard bankCard=new BankCard();
CunThread c=new CunThread(bankCard);
c.setName("线程A");
QuThread q=new QuThread(bankCard);
q.setName("线程B");
c.start();
q.start();
}
7. 线程状态
NEW,====新建状态
RUNNABLE,===>就绪状态和运行状态
BLOCKED,===>堵塞状态
WAITING,====>等待状态
TIMED_WAITING,===>时间等待
TERMINATED;===终止