Java 多线程详解(一)------概念的引入:https://blog.csdn.net/weixin_39816740/article/details/80089790

Java 多线程详解(二)------如何创建进程和线程:https://blog.csdn.net/weixin_39816740/article/details/80089834

介绍完如何创建进程以及线程了,那么我们接着来看一个实例:

  利用多线程模拟 3 个窗口卖票

第一种方法:继承 Thread 类

 创建窗口类 TicketSell 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.ys.thread;
public class TicketSell extends Thread{
    //定义一共有 50 张票,注意声明为 static,表示几个窗口共享
    private static int num = 50;
     
    //调用父类构造方法,给线程命名
    public TicketSell(String string) {
        super(string);
    }
    @Override
    public void run() {
        //票分 50 次卖完
        for(int i = 0 ; i < 50 ;i ++){
            if(num > 0){
                try {
                    sleep(10);//模拟卖票需要一定的时间
                } catch (InterruptedException e) {
                    // 由于父类的 run()方法没有抛出任何异常,根据继承的原则,子类抛出的异常不能大于父类, 故我们这里也不能抛出异常
                    e.printStackTrace();
                }
                System.out.println(this.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
            }
        }
    }
     
}

  创建主线程测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ys.thread;
public class TestTicket {
    public static void main(String[] args) {
        //创建 3 个窗口
        TicketSell t1 = new TicketSell("A窗口");
        TicketSell t2 = new TicketSell("B窗口");
        TicketSell t3 = new TicketSell("C窗口");
         
        //启动 3 个窗口进行买票
        t1.start();
        t2.start();
        t3.start();
    }
}

  结果:这里我们省略了一些,根据电脑配置,结果会随机出现不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
B窗口卖出一张票,剩余48
A窗口卖出一张票,剩余47
C窗口卖出一张票,剩余49
C窗口卖出一张票,剩余46
B窗口卖出一张票,剩余44
A窗口卖出一张票,剩余45
A窗口卖出一张票,剩余43
...
C窗口卖出一张票,剩余5
A窗口卖出一张票,剩余4
B窗口卖出一张票,剩余3
A窗口卖出一张票,剩余2
C窗口卖出一张票,剩余3
B窗口卖出一张票,剩余1
C窗口卖出一张票,剩余0
A窗口卖出一张票,剩余-1

  

第二种方法:实现 Runnable 接口

  创建窗口类 TicketSellRunnable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ys.thread;
public class TicketSellRunnable implements Runnable{
    //定义一共有 50 张票,继承机制开启线程,资源是共享的,所以不用加 static
    private int num = 50;
     
    @Override
    public void run() {
        //票分 50 次卖完
        for(int i = 0 ; i < 50 ;i ++){
            if(num > 0){
                try {
                    //模拟卖一次票所需时间
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
            }
        }
    }
}

  创建主线程测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ys.thread;
public class TicketSellRunnableTest {
    public static void main(String[] args) {
        TicketSellRunnable t = new TicketSellRunnable();
         
        Thread t1 = new Thread(t,"A窗口");
        Thread t2 = new Thread(t,"B窗口");
        Thread t3 = new Thread(t,"C窗口");
         
        t1.start();
        t2.start();
        t3.start();
    }
}

  结果:同理为了篇幅我们也省略了中间的一些结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
B窗口卖出一张票,剩余49
C窗口卖出一张票,剩余48
A窗口卖出一张票,剩余49
B窗口卖出一张票,剩余47
A窗口卖出一张票,剩余45
......
A窗口卖出一张票,剩余4
C窗口卖出一张票,剩余5
A窗口卖出一张票,剩余3
B窗口卖出一张票,剩余2
C窗口卖出一张票,剩余1
B窗口卖出一张票,剩余0
A窗口卖出一张票,剩余-2
C窗口卖出一张票,剩余-1

  

结果分析:这里出现了票数为 负数的情况,这在现实生活中肯定是不存在的,那么为什么会出现这样的情况呢?

  

解决办法分析:即我们不能同时让超过两个以上的线程进入到 if(num>0)的代码块中,不然就会出现上述的错误。我们可以通过以下三个办法来解决:

1、使用 同步代码块

2、使用 同步方法

3、使用 锁机制

①、使用同步代码块

1
2
3
4
5
6
7
8
9
语法:
synchronized (同步锁) {
    //需要同步操作的代码         
}
同步锁:为了保证每个线程都能正常的执行原子操作,Java 线程引进了同步机制;同步锁也叫同步监听对象、同步监听器、互斥锁;
Java程序运行使用的任何对象都可以作为同步监听对象,但是一般我们把当前并发访问的共同资源作为同步监听对象
注意:同步锁一定要保证是确定的,不能相对于线程是变化的对象;任何时候,最多允许一个线程拿到同步锁,谁拿到锁谁进入代码块,而其他的线程只能在外面等着

  实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void run() {
        //票分 50 次卖完
        for(int i = 0 ; i < 50 ;i ++){
            //这里我们使用当前对象的字节码对象作为同步锁
            synchronized (this.getClass()) {
                if(num > 0){
                    try {
                        //模拟卖一次票所需时间
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
                }
            }
             
        }
    }

  

2、使用 同步方法

语法:即用  synchronized  关键字修饰方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
    public void run() {
        //票分 50 次卖完
        for(int i = 0 ; i < 50 ;i ++){
            sell();
             
        }
    }
    private synchronized void sell(){
        if(num > 0){
            try {
                //模拟卖一次票所需时间
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
        }
    }

  注意:不能直接用 synchronized 来修饰 run() 方法,因为如果这样做,那么就会总是第一个线程进入其中,而这个线程执行完所有操作,即卖完所有票了才会出来。

3、使用 锁机制

  

1
public interface Lock

  主要方法:

  常用实现类:

1
2
3
public class ReentrantLock
extends Object
implements Lock, Serializable<br>//一个可重入互斥Lock具有与使用synchronized方法和语句访问的隐式监视锁相同的基本行为和语义,但具有扩展功能。

  例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.ys.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TicketSellRunnable implements Runnable{
    //定义一共有 50 张票,继承机制开启线程,资源是共享的,所以不用加 static
    private int num = 50;
    //创建一个锁对象
    Lock l = new ReentrantLock();
     
    @Override
    public void run() {
        //票分 50 次卖完
        for(int i = 0 ; i < 50 ;i ++){
            //获取锁
            l.lock();
            try {
                if(num > 0){
                //模拟卖一次票所需时间
                Thread.sleep(10);
                System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                //释放锁
                l.unlock();
            }
             
             
        }
    }
    private void sell(){
         
    }
}

  

  

Java 多线程详解(三)------线程的同步相关推荐

  1. Java多线程详解(线程不安全案例)

    嗨喽-小伙伴们我又来了, 通过前面两章的学习,我们了解了线程的基本概念和创建线程的四种方式. 附上链接: 1.  Java多线程详解(基本概念)​​​​​​​ 2. Java多线程详解(如何创建线程) ...

  2. Java多线程详解(线程同步)

    嗨喽-小伙伴们我来了, 上一章,我们通过几个例子(点击跳转)介绍了线程安全问题,而说到线程安全就不得不提到线程同步,它是解决线程安全的一种重要方法.本章就来简单地介绍一下线程同步. 从上一章的学习我们 ...

  3. Java多线程详解(线程池)

    嗨喽-小伙伴们我来了, 上一章我们介绍了Java中的Thread类里一些常用的方法.本节我们就来聊一聊线程池. 说到"池",大家或许都不陌生,在java中,我们有见过数据库连接池, ...

  4. java多线程详解及线程池创建

    多线程 线程 线程是独立的执行路径 在程序执行时,即使自己没有创建线程,后台也会有多个线程 main()称为主线程,为系统的入口,用于执行整个程序 在一个进程中如果开辟了多个线程,线程的运行由调度器安 ...

  5. Java 多线程详解(五)------线程的声明周期

    Java 多线程详解(一)------概念的引入:https://blog.csdn.net/weixin_39816740/article/details/80089790 Java 多线程详解(二 ...

  6. Java 多线程详解(二)------如何创建进程和线程

    Java 多线程详解(一)------概念的引入:https://blog.csdn.net/weixin_39816740/article/details/80089790 在上一篇博客中,我们已经 ...

  7. Java 多线程详解(四)------生产者和消费者

    Java 多线程详解(一)------概念的引入:https://blog.csdn.net/weixin_39816740/article/details/80089790 Java 多线程详解(二 ...

  8. Java多线程详解(基本概念)

    嗨喽-小伙伴们我来啦, 从本章开始,我们就要开始介绍Java中一个非常重要的概念-----多线程.线程化思想是计算机领域的重要思想,有了线程,咱编写的程序才能更为高效准确地运行起来. 首先,咱来了解一 ...

  9. 【运维能力提升计划-3】Java多线程详解

    Java多线程详解 学习链接 Java.Thread 线程简介 线程 进程 多线程 线程实现 Thread 继承Thread类 调用run方法只有主线程一个线程,调用start方法生成子线程与主线程并 ...

最新文章

  1. 【Spark Summit EU 2016】使用Spark和StreamSets构建数据通道
  2. .net core 监听性能,异常
  3. c语言课程设计在哪做,C语言课程设计————写下流程图! 谢谢
  4. 二维绕任意点旋转_二维图形复合线性变换程序设计:三角形绕任意点旋转2wfhbh...
  5. AWS Lambda事件源映射:使您的触发器混乱无序
  6. sqlite3 编译问题
  7. Eclipse设置断点进行调试
  8. 天猫APP也可以点外卖了!饿了么接入天猫App
  9. Redpill:在后渗透中实现反向TCP Shell
  10. 自己常用js方法(DOM操作)
  11. 专业的压缩解压缩工具 WinZip Pro 7 for Mac
  12. 医保支付平台项目建设方案
  13. 史上最全!数据分析进阶教程,看这一篇就够了!
  14. 【笑话】程序员和青蛙公主
  15. 微控制器编程技术c语言,1.单片机C语言编程技术分析.pptx
  16. 图象恢复——(逆滤波,维纳滤波)
  17. 计算机专业河南大学中外合办分数线,河南大学国际教育学院是中外合作办学吗 分数线多少...
  18. 网页插入全屏背景视频
  19. Fintech系列(八) -- 游戏驿站GME股价暴涨事件中的杠杆工具和轧空现象
  20. matlab中集合的表示,Matlab常用命令集合

热门文章

  1. Register Delphi ,Delphi 不能运行
  2. 便利店小程序需要服务器吗,便利店开发小程序的功能
  3. fopen java_fopen()函数
  4. ubuntu 压缩率最高的软件_[图]Linux六大压缩算法横评:Ubuntu 19.10最终选择LZ4
  5. 如何更新 Ubuntu Linux
  6. tshark/wireshark/tcpdump实战笔记(更新中...)
  7. GB28181-2016过检通过
  8. python界面制作和unity的有区别吗_由1~4人利用Sratch、Python、Unity或其它游戏开发工具设计与制作一个的教育类游戏。...
  9. oracle中变量前加冒号_oracle变量的定义和使用【转】
  10. 软件测试---正交试验法