2、第二组
public final void join():当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止
public static native void yield():暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
直接上代码演示:
public class Thread_one { public static void main(String[] args) throws InterruptedException { T t = new T(); Thread thread = new Thread(t); thread.start(); for (int i = 0; i < 15; i++) { Thread.sleep(100); System.out.println("主线程吃了" + (i+ 1) + "颗棒棒糖"); if (i == 4) { System.out.println("让子线程先吃吧"); thread.join(); // thread.yield(); System.out.println("子线程吃完了,该我了"); } } } } class T implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程吃了" + (i + 1) + "颗棒棒糖"); } } }
运行结果:
由代码和运行结果图可以看出:因为语句thread.join();的出现,导致主线程必须等到子线程结束后才能继续执行自己的代码,这就是join()的作用。
五、守护线程
Java中的线程分为两类:一种是守护线程,一种是用户线程。 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
守护线程是用来服务用户线程的,通过在start()方法前调用thread.setDaemon(true)可以把一个用户线程变成一个守护线程。
下面用代码演示一下:
public class Thread_two { public static void main(String[] args) throws InterruptedException { A a = new A(); Thread thread = new Thread(a); //下面的方法要在start方法使用前使用,目的是当我们希望main方法结束后,子线程也可以结束;即把子线程设为守护线性 thread.setDaemon(true); thread.start(); for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("我在疯狂打游戏~"); } } } class A implements Runnable{ @Override public void run() { while (true) { try { Thread.sleep(1000); System.out.println("室友们在努力内卷学习~"); } catch (InterruptedException e) { e.printStackTrace(); } } } }
即当语句我在疯狂打游戏结束之后,守护线程的执行语句室友们在努力内卷学习就结束了,不在执行。
六、生命周期
要想实现多线程,必须在主线程中创建新的线程对象。我们可以使用Thread类
及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五
种状态:
新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建
状态
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已
具备了运行的条件,只是没分配到CPU资源
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线
程的操作和功能
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中
止自己的执行,进入阻塞状态
死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
代码演示一下:
public class Thread_three { public static void main(String[] args) throws InterruptedException { B b = new B(); Thread thread = new Thread(b); System.out.println(thread.getName() + "状态为" + thread.getState()); thread.start(); //只要子线程状态不是终止状态,就继续查看子线程的状态 while (Thread.State.TERMINATED != thread.getState()) { System.out.println(thread.getName() + "状态" + thread.getState()); Thread.sleep(500); } System.out.println(thread.getName() + "状态" + thread.getState()); } } class B implements Runnable { @Override public void run() { while (true) { for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); System.out.println("我说了" + (i + 1) + "次Hi"); } catch (InterruptedException e) { e.printStackTrace(); } } break; } } }
运行结果:
七、线程的同步
下面案例都以模仿卖票入手,解决卖超的情形
1、方式一
同步方法
public class Thread_four { public static void main(String[] args) { Ticket ticket = new Ticket(); Thread thread1 = new Thread(ticket); Thread thread2 = new Thread(ticket); thread1.start(); thread2.start(); } } class Ticket implements Runnable{ private int tickets = 30; @Override public synchronized void run() { while (true) { try { if (tickets <= 0) { System.out.println("票没了,售票结束!"); break; } System.out.println("窗口" + Thread.currentThread().getName() + "出售了一张票,还剩下" + --tickets + "张票"); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果:
2、方式二
同步代码块
class Ticket implements Runnable { private int tickets = 30; @Override public void run() { synchronized (this) { while (true) { try { if (tickets <= 0) { System.out.println("票没了,售票结束!"); break; } System.out.println("窗口" + Thread.currentThread().getName() + "出售了一张票,还剩下" + --tickets + "张票"); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
八、锁
1、死锁
public class Thread_five { public static void main(String[] args) { DeadLock deadLock1 = new DeadLock(true); DeadLock deadLock2 = new DeadLock(false); Thread thread1 = new Thread(deadLock1); Thread thread2 = new Thread(deadLock2); thread1.start(); thread2.start(); } } class DeadLock implements Runnable { static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static static Object o2 = new Object(); boolean flag; public DeadLock(boolean flag) { this.flag = flag; } @Override public void run() { //下面业务逻辑的分析 //1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁 // 2. 如果线程 A 得不到 o2 对象锁,就会 Blocked // 3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁 // 4. 如果线程 B 得不到 o1 对象锁,就会 Blocked if (flag) { synchronized (o1) {//对象互斥锁, 下面就是同步代码 System.out.println(Thread.currentThread().getName() + " 进入 1"); synchronized (o2) { // 这里获得 li 对象的监视权 System.out.println(Thread.currentThread().getName() + " 进入 2"); } } } else { synchronized (o2) { System.out.println(Thread.currentThread().getName() + " 进入 3"); synchronized (o1) { // 这里获得 li 对象的监视权 System.out.println(Thread.currentThread().getName() + " 进入 4"); } } } } }
运行结果:
2、释放锁
因为不好演示,所以就介绍一下释放锁的各种情况:
当前线程的同步方法、同步代码块执行结束
当前线程在同步代码块、同步方法中遇到break、return
当前线程在同步代码块、同步方法中出现了未处理的ERROR、Exception,导致异常结束
最后再介绍不会释放锁的情况:
线程执行同步代码块或同步方法时,程序调用sleep()、yield()暂停当前线程的执行,不会释放锁
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程也不会释放锁
总结
乐莫乐兮新相知,很高兴各位小伙伴可以坚持看完这篇文章。整理的有点累了,如果对你有帮助,可以给博主三连支持一下哦~~ 一起加油,一起进步!