body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

某电影院正在上映《速度与激情7》,共有100张票。它又三个售票窗口正在售票。请设计一个应用程序来模拟该电影院的售票
两种方式实现:继承Thread类;实现Runable接口

两种实现方式的对比:
方法1:需要多个对象
方法2:只需要新建一个对象即可,放入三个不同线程;实现了数据和业务模型的分离

该程序跟实际情况还有一些距离,因为实际情况下,售票数据通过网络传输,总是存在一些延迟的情况。所以在真正售出一张票后,需要一段时间,才可以真正去修改剩余票数。
继续更新我们的代码:每次买票延迟100ms,后再去修改剩余票数

问题1
相同的票 卖了多次
问题2
出现了负数的票数
注意:线程安全问题在理想情况下不易出现,但是一旦出现,影响将非常大

如何解决线程安全问题:
分析出问题的原因:多线程,共享数据,操作共享数据并非原子操作(是否有多条语句)
解法:将出问题的原因或条件破坏掉

解决问题: 
同步代码块
格式:
Synchronized(对象){   //让这里的代码变成一个原子操作,不会再代码块的某一个地方切换到其他线程;对象可以是Object
        需同步代码块
}
同步代码块的对象可以是?
需要同步的代码块是?

MainClass.java
package com.java.ticket;
public class MainClass {
        public static void main(String[] args) {
                //方法一  
                Window window1=new Window(100);
                Window window2=new Window(100);
                Window window3=new Window(100);
                window1.start();
                window2.start();
                window3.start();
                //方法二  sellTicket共享,就不用把ticket声明成静态的 ; 同样,没有互斥
/*             SellTicket sellTicket=new SellTicket();
                Thread t1=new Thread(sellTicket,"窗口1");   //用同一个对象初始化三个线程,并每个线程命名
                Thread t2=new Thread(sellTicket,"窗口2");
                Thread t3=new Thread(sellTicket,"窗口3");
                t1.start();
                t2.start();
                t3.start();*/
        }
}
Window.java SellTicket.java
package com.java.ticket;
public class Window extends Thread {
        static int ticket;
        Object object=new Object();
        public void run() {
                super.run();
                while(ticket>0){
                        //卖票
                        //方式一:
                /*     synchronized (object){
                        sell();
                        }*/
                        //方式二
                        sell();
                }
        }
/*          void sell(){
                if(ticket>0){
                        System.out.println(getName()+"卖出第 "+ticket--+" 张票");  //执行这条语句的同时,共享数据自减
             }
        }*/
        public synchronized void sell(){    //最佳
                if(ticket>0){
                        System.out.println(getName()+"卖出第 "+ticket--+" 张票");  //执行这条语句的同时,共享数据自减,属于一个原子操作
                }
        }
        public Window(int ticket) {
                super();
                this.ticket = ticket;
        }
}
package com.java.ticket;
public class SellTicket implements Runnable {
        Object object=new Object();
        int ticket=100;
        public void run() {
                 //synchronized(object){   //让这里的代码变成一个原子操作,不会再代码块的某一个地方挂起切换到其他线程
                        //同步语句在这里就只有一个窗口卖票,其他窗口无法打断
                        while(ticket>0){
                          //出现卖负票是因为在这里挂起
                          //synchronized(object){    //同步语句加在这里,会出现卖负票
                                synchronized(object){  //括号里(new Object)就不行,会出现多张相同的票;因为每个线程调用run方法,都会new一个对象,多个线程就会有多个锁
                                        //这里object也可以是this,因为三个线程都是用同一个对象来初始化的,所以obj也不用声明成静态的
                                        if(ticket>0){
                                                System.out.println(Thread.currentThread().getName()+"卖出第 "+ticket--+" 张票");
                                                //ticket--;
                                        }                                       
                      }                       
                  }
        }
}
test1.java test2.java
package com.java.threadExercise;
import java.util.Random;
public class test2 {
        /**
         * 创建一个任务,它将睡眠1到10秒之间的随机数量的时间,
         * 然后显示它的睡眠时间并退出。创建并运行多个这种任务。
         */
        public static void main(String[] args) {
                Thread1 t1=new Thread1("No.1");
                Thread1 t2=new Thread1("No.2");
                Thread1 t3=new Thread1("No.3");
                t1.start();
                t2.start();
                try {
                        t2.join();  //等待线程t2执行完毕才会执行下面的语句;开始t3线程
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
                t3.start();
        }
}
class Thread1 extends Thread {
        public Thread1(String string){
                super(string);
        }
        public void run(){
                super.run();
                Random r1=new Random();
                int i=r1.nextInt(11)*1000;   //随机生成10以内的数,后面sleep里面要是毫秒,所以乘以1000
                try {
                        sleep(i);
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" 睡眠时间: "+i/1000);
        }
}

package com.java.threadExercise;
import java.util.Random;
public class test3 {
        /**
         * 将所有线程修改成守护线程,并验证一旦main函数退出,程序立刻终止。
         */
        public static void main(String[] args) {
                Thread2 t1=new Thread2("No.1");
                Thread2 t2=new Thread2("No.2");
                Thread2 t3=new Thread2("No.3");
                t1.setDaemon(true);
                t2.setDaemon(true);
                t3.setDaemon(true);
                t1.start();
                t2.start();
                t3.start();
                System.out.println("主线程结束。。。。");
        }
}
class Thread2 extends Thread {
        public Thread2(String string){
                super(string);
        }
        public void run(){
                super.run();
                Random r1=new Random();
                int i=r1.nextInt(11)*1000;   //随机生成10以内的数,后面sleep里面要是毫秒,所以乘以1000
                try {
                        sleep(i);
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" 睡眠时间: "+i/1000);
        }
}

test3.java //test3.java
public class test4 {
        public static void main(String[] args) {
                Scanner in=new Scanner(System.in);
                int num=in.nextInt();
                Object obj=new Object();
                int x=in.nextInt();
                Thread3.getValue(num, x);
                for(int i=0;i<num;++i){
                        new Thread3("第"+(i+1)+"个下载线程").start();      
                }
        }
}
class Thread3 extends Thread{
        public static int Num;
        public static int X;
        static Object obj=new Object();
        public Thread3(String string){
                super(string);
        }
        public static void getValue(int num,int x){
                Num=num;
                X=x;
        }
        public void run() {
                super.run();
                while(X>=0){
                        synchronized(obj){
                  //注意 ,这里要Obj必须是同一个对象才能实现加锁,不然会出现多把锁,失去意义;所以如果有多个线程对象,obj要声明成静态的              
                                if(X<=0){
                                        System.out.println("下载完成");
                                        System.exit(0);
                                }
                                System.out.println(this.getName()+" :剩余"+X+"M未下载");
                                X--;
                        }
                }
        }
}

public class test4 {
        public static void main(String[] args) {
                Scanner in=new Scanner(System.in);
                int num=in.nextInt();
                Object obj=new Object();
                int x=in.nextInt();
                Thread3.getValue(num, x);
                Thread3 t3=new Thread3();
                for(int i=0;i<num;++i){
                        new Thread(t3,"第"+(i+1)+"个下载线程").start();   //用同一个对象初始化线程,可以共用数据,下面run方法就可以不用把obj定义成static,因为所有线程都共用数据
                }
        }
}
class Thread3 implements Runnable{
        public static int Num;
        public static int X;
       Object obj=new Object();   //不用定义成静态的
        public static void getValue(int num,int x){
                Num=num;
                X=x;
        }
        public void run() {
                while(X>=0){
                        synchronized(obj){
                  //this也可以,因为是一个对象初始化的线程
                                if(X<=0){
                                        System.out.println("下载完成");
                                        System.exit(0);
                                }
     System.out.println(Thread.currentThread().getName()+" :剩余"+X+"M未下载");
                                X--;
                        }
                }
        }
}


Java——多线程小例子的更多相关文章

  1. Java多线程小例子(三个窗口卖火车票)

    class Ticket implements Runnable{ private int TicketNum = 100; //100张火车票 private boolean flag = true ...

  2. java多线程小题一瞥

    有如下线程类定义: public class MyThread extends Thread { private static int num = 0; public MyThread() { num ...

  3. java 多线程简单例子

    实现线程的方式是一,继承Thread类,重写父类的run()方法 二,实现接口Runnable中的run()方法. 下面是简单的例子 例子1:银行存取钱问题 package com.direct.de ...

  4. 【多线程】java多线程 测试例子 详解wait() sleep() notify() start() join()方法 等

    java实现多线程,有两种方法: 1>实现多线程,继承Thread,资源不能共享 2>实现多线程  实现Runnable接口,可以实现资源共享 *wait()方法 在哪个线程中调用 则当前 ...

  5. Java多线程小总结

    多线程 线程与进程 线程:具有完成特定任务的一条执行路径,是CPU执行的最小单位 进程:正在执行的程序 重点:CPU在某个时间刻度上只能够执行一条原子性语句 字节最小是bit位 原子性语句:不能够再次 ...

  6. java反射小例子

    package com.txwsqk.reflect; public class Car { private String brand; private String color; private i ...

  7. JSP调用JAVA方法小例子

    用JAVA编写的函数 package doc; //定义一个包 public class Dy { //定义一个类 public static int Sub(int x,int y){ //定义函数 ...

  8. java算法小例子

    作为一个程序员,有时候我觉得自己都不适合,因为自己数学不好,算法不好,脑子不够灵活.而对于算法,感觉就像是数学题,总觉得很难.以前上学,在班里总有几个什么都不好,但唯独数学很厉害,真气人!面对难题时, ...

  9. LigerUI java SSH小例子

    1.新建web project 2.ssh框架 加入到项目中去(这里不介绍,网上搜索) 3.struts2配置 http://www.cnblogs.com/istianyu/archive/2013 ...

随机推荐

  1. 【入门详解】MyBatis入门基础详解

    什么是mybatis? MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索.MyBatis使用简单的XM ...

  2. c#进阶之Delegate

    委托是什么?答:委托是一种类型   等同与 一个class类,继承System.MulticastDelegate,但mult....gate是一个特殊类,不能够派生 委托的调用,如何去使用 1/委托 ...

  3. 论文笔记—Flattened convolution neural networks for feedforward acceleration

    1. 论文思想 一维滤过器.将三维卷积分解成三个一维卷积.convolution across channels(lateral), vertical and horizontal direction ...

  4. 【TCP/IP详解 卷一:协议】TCP的小结

    前言:TCP学习的综述 在学习TCP/IP协议的大头:TCP协议 的过程中,遇到了很多机制和知识点,详解中更是用了足足8章的内容介绍它. TCP协议作为 应用层 和 网络层 中间的 传输层协议,既要为 ...

  5. 视图层view layer

    视图层是Django处理请求的核心代码层,我们大多数Python代码都集中在这一层面. 它对外接收用户请求,对内调度模型层和模版层,统合数据库和前端,最后根据业务逻辑,将处理好的数据,与前端结合,返回 ...

  6. 基于Socket的Android手机视频实时传输

    首先,简单介绍一下原理.主要是在手机客户端 (Android)通过实现Camera.PreviewCallback接口,在其onPreviewFrame重载函数里面获取摄像头当前图像数据, 然后通过S ...

  7. 用docker部署gitlab

    docker hub官网下载gitlab速度太慢,改用国内镜像+中文版 docker pull registry.cn-hangzhou.aliyuncs.com/lab99/gitlab-ce-zh ...

  8. resin中关于url rewrite来传递jsessionid的问题

    最近两天在项目中碰到,一个很奇怪的问题.同一个账号多次切换登录时,会出现这个账号的信息在session中找不到,虽然可以登录成功,但是之后这个用户信息好像没有保存到session中一样,或者是被改变了 ...

  9. Unity 代码优化

    1.不用的代码删除掉,因为即使不用的代码也会 IL2Cpp. 2.MonoBehaviour 的方法是空方法,特别是Update方法,删除掉,会有性能消耗. 3.Unity 中 override 的方 ...

  10. Java 包的概述和讲解

    2017-11-02 22:58:45 包(package):其实就是文件夹. 包的作用是对类进行分类的管理,并且区分不同的类名. 举例: 学生:增加,删除,修改,查询 教师:增加,删除,修改,查询 ...