多线程(下)
线程的同步
- 例:卖票问题:创建三个窗口卖票,总票数为100张,使用实现Runnable接口的方式
- 问题:卖票过程中,出现了重票、错票-->出现了线程的安全问题
- 问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
- 如何解决:当一个线程在操作ticket的时候,其他线程不能参与进来。直到线程操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程出现了阻塞也不能改变。
- 在Java中,我们通过同步机制,来解决线程安全问题
- 方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:1.操作共享数据的代码,即为需要被同步的代码 2.共享数据: 多个线程共同操作的变量 比如ticket 3.同步监视器,俗称,锁。任何一个类的对象,都可以充当锁(锁的要求:多个线程必须要公用同一把锁。4.在实现Runnable接口创建多线程的方式中,可以使用this充当同步监视器5.在使用Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器
- 方式二:同步方法:如果操作共享数据 代码完整的声明在一个方法中,我们不妨将此方法声明为同步的
总结:使用同步方法的总结
- 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
- 非静态的同步方法,同步监视器是:当前类本身
- 同步的方式,解决了线程的安全问题。---好处,操作同步代码时,只能有一个线程参与,其他线程等待,相当于是一个单线程过程,效率低。---局限性
| |
| public class WindowTest1 { |
| public static void main(String[] args) { |
| Window window = new Window(); |
| Thread t1 = new Thread(window); |
| Thread t2 = new Thread(window); |
| Thread t3 = new Thread(window); |
| t1.setName("窗口1"); |
| t2.setName("窗口2"); |
| t3.setName("窗口3"); |
| t1.start(); |
| t2.start(); |
| t3.start(); |
| } |
| } |
| class Window implements Runnable{ |
| private int ticket=100; |
| |
| |
| @Override |
| public void run() { |
| while (true){ |
| |
| |
| |
| synchronized (this){ |
| if (ticket>0){ |
| try { |
| Thread.sleep(10); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| System.out.println(Thread.currentThread().getName()+"卖"+ticket+"号票"); |
| ticket--; |
| }else{ |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| public class WindowTest2 { |
| public static void main(String[] args) { |
| Window2 w1 = new Window2(); |
| Window2 w2 = new Window2(); |
| Window2 w3 = new Window2(); |
| w1.setName("线程一"); |
| w2.setName("线程二"); |
| w3.setName("线程三"); |
| w1.start(); |
| w2.start(); |
| w3.start(); |
| } |
| } |
| class Window2 extends Thread{ |
| public static int ticket= 100; |
| public static Object obj = new Object(); |
| |
| @Override |
| public void run() { |
| while (true){ |
| |
| |
| synchronized (Window2.class){ |
| |
| try { |
| Thread.sleep(100); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| if (ticket>0){ |
| System.out.println(Thread.currentThread().getName()+"卖第"+ticket+"张票"); |
| ticket--; |
| }else{ |
| break; |
| } |
| } |
| |
| } |
| } |
| } |
| |
| public class WindowTest3 { |
| public static void main(String[] args) { |
| Window3 w = new Window3(); |
| Thread t1 = new Thread(w); |
| Thread t2 = new Thread(w); |
| Thread t3 = new Thread(w); |
| t1.start(); |
| t2.start(); |
| t3.start(); |
| } |
| } |
| class Window3 implements Runnable{ |
| private int ticket = 100; |
| |
| @Override |
| public void run() { |
| while (ticket>0){ |
| show(); |
| } |
| } |
| private synchronized void show(){ |
| |
| if (ticket>0){ |
| System.out.println(Thread.currentThread().getName()+"卖出第"+ticket+"张票"); |
| ticket--; |
| try { |
| Thread.sleep(100); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| |
| public class WindowTest4 { |
| public static void main(String[] args) { |
| Window4 w1 = new Window4(); |
| Window4 w2 = new Window4(); |
| Window4 w3 = new Window4(); |
| w1.setName("窗口一"); |
| w2.setName("窗口二"); |
| w3.setName("窗口三"); |
| w1.start(); |
| w2.start(); |
| w3.start(); |
| } |
| } |
| class Window4 extends Thread{ |
| private static int ticket = 1000; |
| |
| @Override |
| public void run() { |
| while (ticket>0){ |
| show(); |
| } |
| } |
| private static synchronized void show(){ |
| if (ticket>0){ |
| try { |
| Thread.sleep(1); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| System.out.println(Thread.currentThread().getName()+"卖出第"+ticket+"票"); |
| ticket--; |
| } |
| } |
| } |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!