2.3. Thread 常用的方法
2.3.1. 线程休眠方法--- sleep
sleep 线程的休眠方法 -------static void sleep(静态方法)
public class MyThread extends Thread{
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < 20; i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
2.3.2. yield 当前线程让出cpu-参与下次的竞争
当前正在执行的线程暂停执行,并允许其他线程获得执行的机会。具体来说,它会让出CPU时间片给其他线程,但并不保证一定会让出。
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++){
Thread.yield();
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
2.3.3. join------加入当前线程上
插入的线程执行完毕后,当前线程才会执行。
当在一个线程中调用另一个线程的 join() 方法时,当前线程会被阻塞,直到被调用的线程执行完毕。这样可以确保在继续执行后续代码之前,被调用的线程已经完成了它的任务。
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread2 t2 = new MyThread2();
t1.start();
t2.start();
try {
t1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for(int i = 0; i < 20; i++){
System.out.println("main线程:" + i);
}
}
}
2.3.4. setDaemon()-----------设置线程为守护线程
当所有线程执行完毕后,守护线程也会终止。
JDK--默认就有一个守护线程.GC垃圾回收。
'public static void main(String[] args) {
Mythread t1 = new Mythread();
t1.setName("线程1");
t1.setDaemon(true);
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println("main线程:"+i);
}
}
其他方法:
- run(): 当线程被启动时,执行该方法中的代码。如果线程是使用 Runnable 接口创建的,则 run() 方法来源于该接口的实现。
- start(): 用于启动线程,会调用线程对象的 run() 方法。如果线程已经启动,再次调用 start() 会抛出 IllegalThreadStateException 异常。
- currentThread(): 返回对当前正在执行的线程对象的引用。这在需要获取当前线程信息或者对当前线程进行操作时非常有用。
- sleep(long millis) / sleep(long millis, int nanos): 让当前线程休眠指定的时间。第一个参数是以毫秒为单位,第二个参数是以纳秒为单位。在休眠期间,线程不丧失任何监视器的所有权。
- yield(): 提示调度程序当前线程愿意放弃处理器的使用,但调度程序可以忽略这个提示。通常用于实现线程之间的协作,让出 CPU 时间给其他线程。
- interrupt(): 用于中断线程的执行。如果线程处于阻塞状态(例如,通过调用 sleep() 方法),那么该线程的 interrupt() 方法会被调用,从而抛出 InterruptedException。
- join(): 等待指定线程终止。例如,在一个线程 A 中调用另一个线程 B 的 join() 方法,则线程 A 将进入阻塞状态,直到线程 B 运行结束。
- isAlive(): 检查线程是否仍然存活。如果线程已经启动且尚未结束,则返回 true。
- getName() / setName(String name): 获取或设置线程的名称。线程名称对于调试和监控非常有用。
- setPriority(int newPriority): 更改线程的优先级。优先级高的线程更可能得到执行。
- isInterrupted(): 测试线程是否已中断,返回布尔值。与 interrupted() 方法不同,这个方法不会清除中断状态。
- interrupted(): 测试当前线程是否已中断,并清除中断状态。如果连续两次调用这个方法,第二次调用将返回 false,除非当前线程在这两次调用之间再次被中断
2.4. 解决线程安全问题
2.4.1. 什么情况下会出现线程的安全问题?
当多个线程操作同一个资源时,则出现线程安全问题。
2.4.2. java如何解决线程安全问题
提供了两种方式:
第一种: 使用synchronized自动锁
第二种: 使用Lock手动锁。
使用锁相对于把原来的异步转换为同步操作。
使用synchronized自动锁 -------(它可以使用在方法上,也可以使用在代码块中。)
用法 1 代码块 : ---- (这里需要注意 参数对象需要是 同一个 )
synchronized(共享锁对象){
//同步代码块。
}
用法 2 方法上 --- 直接在返回值类型 前加 synchronized
public class SellTickets implements Runnable{
private Integer ticket = 100;
@Override
public void run() {
while (ticket > 0){
sellTicket();
}
}
public synchronized void sellTicket() {
if(ticket > 0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "正在出售第" + (100-ticket) + "张票");
}
}
}
使用Lock手动锁
public class SellTicket2 implements Runnable{
private Integer ticket = 10000;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();
if(ticket > 0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "正在出售第" + (10000-ticket) + "张票");
}else {
break;
}
} finally {
lock.unlock();
}
}
}
}
2.4.3. synchronized和lock区别
syn可以使用代码块和方法。自动加锁和释放锁。不会出现死锁问题。
lock它只能使用在代码块中。需要手动加锁和释放锁。如果不释放锁,死锁问题。灵活。它的释放锁必须放在finally.
2.5. 线程的死锁问题
线程A拥有锁资源a,希望获取锁资源b,线程B拥有锁资源b,希望获取锁资源a。 两个线程互相拥有对方希望获取的锁资源。可能会出现程序堵塞。从而造成死锁。
解决办法
- 不要使用锁嵌套。
- 设置超时时间。--Lock类中tryLock.
- 使用安全java.util.concurrent下的类。
2.6. 线程通信 ---- 通过调用不同的方法相互转换
主要用到 wait() notify() 方法
wait()方法使当前线程进入等待状态,直到其他线程调用同一个对象的notify()
notify()方法用于唤醒在此对象监视器上等待的单个线程。如果有多个线程正在等待,则随机选择一个线程唤醒。需要注意的是,调用notify()方法并不会立即释放锁,而是在synchronized代码块或方法执行完毕后才会真正释放锁。
2.7. 线程状态
NEW,====新建状态。
RUNNABLE,===>就绪状态和运行状态
BLOCKED,===>堵塞状态
WAITING,====>等待状态
TIMED_WAITING,===>时间等待
TERMINATED;===终止。