精华内容
下载资源
问答
  • java多线程实例
    2021-09-15 01:09:41

    Java多线程加锁例子


    一、错误示例

    
        static int a = 0; //多线程同时操作 val a
    
        
        @Test
        public void test4() throws InterruptedException {
            Runnable runnable = () -> {
    			while (a < 10) {
    				a++;
    				System.out.print(a + "\t");
    			}
            };
            Thread t1 = new Thread(runnable);
            t1.start();  //线程t1 启动
    
            Thread t2 = new Thread(runnable);
            t2.start();  //线程t2 启动
    
            Thread t3 = new Thread(runnable);
            t3.start();  //线程t3 启动
    
        }
    

    输出结果: 1 3 4 6 7 8 9 10 2 6
    原因:
    1.代码可见 我用lambda实现了runnable的实现类
    2.下面创建了三个线程分别对同一个runnable变量进行start启动,让jvm去跑这个runnable

    这时候三个线程是不同的
    但是线程里面有个while条件,注意where条件
    a如果小于10,就进循环,然后 a++ 了
    这时候我说有bug,你听我细细道来,

    假如第一个线程t1 第一次进来a是小于10的…因为第一次a=0
    然后t1线程还没有执行到a++的时候t2又进来了
    …这时候t1能放过t2吗?
    t2给t1说:你管得着吗?你又没有关门,凭什么不让我进来,
    这时候t1执行了a++, 但是t1还没有执行输出,t2也执行了a++
    输出了:1 (反正不知道是哪个线程输出的)
    输出了:3 (我估计是t3线程的干扰)

    二、正确示例

    
        static int a = 0; //多线程同时操作 val a
        static final Object obj = new Object(); //锁对象
        
        @Test
        public void test4() throws InterruptedException {
            Runnable runnable = () -> {
                synchronized (obj) {
                    while (a < 10) {
                        a++;
                        System.out.print(a + "\t");
                    }
                }
            };
            Thread t1 = new Thread(runnable);
            t1.start();  //线程t1 启动
    
            Thread t2 = new Thread(runnable);
            t2.start();  //线程t2 启动
    
            Thread t3 = new Thread(runnable);
            t3.start();  //线程t3 启动
    
        }
    

    输出结果: 1 2 3 4 5 6 7 8 9 10
    原因:
    static final Object obj = new Object();
    synchronized (obj) {

    }
    每个线程执行到synchronized代码块的时候,都会把同一个变量 锁上
    因为是static修饰的,所以多个线程都会同意这个锁是同一个锁
    因为每个线程进来之前都会去看看这个锁对象有没有被占用
    所以实现了分离

    t1进来了,其他都进不去,当t1跑完循环,离开了 t2进来了,但是条件不满足了,a已经不小于10了
    所以这个正确的结果是同一个线程完成的

    更多相关内容
  • Java多线程实例

    2020-09-03 04:34:38
    本文给大家介绍java多线程实例,对java多线程知识感兴趣的朋友参考下吧
  • 详细的讲解了java多线程的原理,并配有代码进行实战,适合java初学者和想对多线程有进一步了解的人。
  • 主要介绍了Java多线程并发执行demo代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • @ Java组第3次交流讲件 线程 熊斌斌!Wncy ./javaDocs/htmlzh_cn/api/ @ 字符串 字符串概述 字符串是由字符组成的序列 Java中的字符串分为 定长字符串(Stng)和 可变字符串( String Buffer) abcdefghijklmnopgrstuwxyz...
  • java多线程使用详解与案例,超详细

    千次阅读 2021-03-14 17:31:47
    java多线程使用 java使用多线程的三种方式: 继承Thread类,并重写run方法。 实现Runnable接口,重写run方法 实现Callable<返回值>接口,重写call方法 实例如下 继承Thread class test extends Thread{ @...


    java多线程使用

    线程lamda表达式方式启动(简单、常用)

    new Thread(()->{
                System.out.println("开始了一个线程"+new Date());
            }).start();
    

    java使用多线程的三种方式:

    1. 继承Thread类,并重写run方法。
    2. 实现Runnable接口,重写run方法
    3. 实现Callable<返回值>接口,重写call方法

    实例如下

    继承Thread

    class test extends Thread{
        @Override
        public void run() {
            System.out.println("你好啊");
        }
    }
    

    开始启动线程:

    new test().start()
    

    实现Runnable

    class God implements Runnable{
    
        @Override
        public void run() {
            while (true)
            {
                System.out.println("上帝守护着你");
            }
        }
    }
    

    开始启动线程

    new Thread(New God()).start
    

    实现Callable

    class dThread implements Callable<Boolean> {
        private String url;
        private String name;
        public dThread(String url,String name)
        {
            this.url=url;
            this.name= name;
        }
        @Override
        public Boolean call() {
            Download d = new Download();
            System.out.println(Thread.currentThread().getName()+"开始下载");
            d.imageDownload(url,name);
            return Thread.currentThread().isAlive();
        }
    }
    

    开始启动线程

    FutureTask <String>threadTask= new FutureTask<>(new dThread());
    new Thread(threadTask).start
    

    开始执行线程只需在线程对象调用start或call方法便开始执行线程

    #####################################################

    线程池的使用:

    我们需要使用ExecutorService对象里面

    一个方法newFixedThreadPool(5);,参数为线程存放个数。
    实例对象调用execute方法,参数为runable对象
    使用案例如下

    package cn.xjt.gaoji;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 测试线程池
     * @author xujiangtao
     * @create 2020-08-02-14:42
     */
    public class TestPool {     //pool:池子
        public static void main(String[] args) {
            //创建了一个大小为10的线程池
            //固定线程池
            ExecutorService e = Executors.newFixedThreadPool(5);
            //单线程池
            Executors.newSingleThreadExecutor();
            //可缓存线程池
            Executors.newCachedThreadPool();
            //时间周期线程池
            Executors.newScheduledThreadPool(6);
            //工作窃取线程池
            Executors.newWorkStealingPool(2);		//可以指定并行数
            //调用execute方法开启线程
            e.execute(new MyThread());
            //execute:实行
            e.execute(new test());
            e.execute(new MyThread());
            e.execute(new MyThread());
            e.shutdown();                   //关闭线程
        }
    }
    class MyThread implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
    
    class test extends Thread{
        @Override
        public void run() {
            System.out.println("你好啊");
        }
    }
    

    守护线程:

    daemon守护线程会因为用户线程关闭而关闭,默认为用户线程
    Thread实例对象setDarmon()方法可以设置是否为守护者线程,默认为false。

    守护线程会随着用户线程的结束而结束
    java守护线程案例如下:

    package cn.xjt.demo03;
    
    /**
     * daemon守护线程会因为用户线程关闭而关闭,默认为用户线程
     * y
     * @author xujiangtao
     * @create 2020-08-01-10:36
     */
    public class TestDaemon {
        public static void main(String[] args) {
            God god = new God();
            HandsomeYou handsomeyou = new HandsomeYou();
            Thread thread = new Thread(god);
            thread.setDaemon(true);         //上帝守护线程
            thread.start();
            new Thread(handsomeyou).start();
        }
    }
    class God implements Runnable{
    
        @Override
        public void run() {
            while (true)
            {
                System.out.println("上帝守护着你");
            }
        }
    }
    class HandsomeYou implements Runnable{
    
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("Hello world 你开心的活着");
            }
            System.out.println("======goodbye world========");
        }
    }
    
    

    使用lamda表达式简化java的书写简单化案例如下:

    package cn.xjt.demo03;
    
    /**
     * @author xujiangtao
     * @create 2020-07-31-20:10
     */
    public class TestLamda {
        //第1步简化->静态内部类
        static class son2 implements Like{
    
            @Override
            public void lamda() {
                System.out.println("i'm lamda2");
            }
        }
        public static void main(String[] args) {
            Like s = new son1();
            s.lamda();
            s=new son2();
            s.lamda();
            //第2步简化->局部内部类
            class son3 implements Like{
    
                @Override
                public void lamda() {
                    System.out.println("i'm lamda3");
                }
            }
            s=new son3();
            s.lamda();
            //第3步简化->匿名内部类
            s=new Like() {
                @Override
                public void lamda() {
                    System.out.println("i'm Lamda4");
                }
            };
            s.lamda();
            //第4步简化->Lamda
            s= ()-> {
                    System.out.println("i'm Lamda5");
                };
            s.lamda();
        }
    }
    //接口类
    interface Like{
        void lamda();
    }
    //正常实现类
    class son1 implements Like{
    
        @Override
        public void lamda() {
            System.out.println("i'm lamda");
        }
    }
    
    

    多线程综合案例

    1、买票案例

    使用生产者消费者模式。通过缓存区,保证产品数量大于消费数量

    package cn.xjt.gaoji;
    
    /**
     * 测试生产者消费者-->利用缓冲区:管程法
     * @author xujiangtao
     * @create 2020-08-01-21:53
     */
    //成产者,消费者,产品,缓存区
    public class TestPC {
        public static void main(String[] args) {
            Syncontainer syncontainer = new Syncontainer();
            new Productor(syncontainer).start();
            new Consumer(syncontainer).start();
    
        }
    }
    //生产者
    class Productor extends Thread{
        Syncontainer container;
        public Productor(Syncontainer container)
        {
            this.container=container;
        }
        //生产
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    container.push(new Chicken(i));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产了"+(i+1)+"只鸡");
            }
        }
    }
    //消费者
    class Consumer extends  Thread{
        Syncontainer container;
        public Consumer(Syncontainer container)
        {
            this.container=container;
        }
        //消费
    
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println("消费了->"+container.pop().id+"只鸡");
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //产品
    class Chicken{
        int id;
        Chicken(int id)
        {
            this.id=id;
        }
    }
    //缓冲区
    class Syncontainer{
        //需要一个容器大小
        Chicken[] chickens = new Chicken[10];
        int count =0;
        //生产者放入产品
        public synchronized void push(Chicken chicken) throws InterruptedException {
            //如果容器满了,需要消费者消费
            if(count ==chickens.length)
            {
                this.wait();
            }
    
            //如果没有满,就需要生产产品放入
            chickens[count] = chicken;
            count++;
    
            //可以通知消费者了
            this.notifyAll();
        }
        //消费者消费产品
        public synchronized Chicken pop() throws InterruptedException {
            //判断缓冲区是否还有产品
            if(count==0)
            {
                //等待生产者生产,消费者等待
                this.wait();
            }
            count--;
            return chickens[count];
    //        this.notifyAll();
            //吃完了,通知生产者生产
        }
    }
    

    2、信号灯法,通过标志为解决

    package cn.xjt.gaoji;
    
    /**
     * 测试生产者,消费者问题 2:信号灯法
     * 标志位解决
     * @author xujiangtao
     * @create 2020-08-02-13:11
     */
    public class TestPC2 {
        public static void main(String[] args) {
            TV tv = new TV();
            new actor(tv).start();
            new watcher(tv).start();
        }
    }
    //生产者->演员
    class actor extends Thread{
        TV tv;
        public actor(TV tv)
        {
            this.tv=tv;
        }
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    if(i%2==0)
                    {
                        tv.play("抖音:记录美好生活");
                        System.out.println(i);
                    }else{
                        tv.play("快乐大本营");
                        System.out.println(i);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //消费者->观众
    class watcher extends Thread{
        TV tv;
        public watcher(TV tv)
        {
            this.tv=tv;
        }
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    tv.watch("观众");
                    System.out.println(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //产品->Tv
    class TV{
        String video;
    //    String Watchername;
        boolean flag = true;
        //演员表演,观众等待 T
        //观众观看,演员等待 F
        //表演方法
        public synchronized void play(String vidio) throws InterruptedException {
            //判断如果是true,则表演节目
            if(flag)
            {
                System.out.println("演员表演了->"+vidio);
                Thread.sleep(1000);
                //通知观众观看
                this.notifyAll();   //通知观众观看节目
                this.video=vidio;
                flag=!this.flag;
            }else { //否则,等待观众观看
                this.wait();
            }
        }
        public synchronized void watch(String watchername) throws InterruptedException {
            //判断如果为flase,则观众观看节目
            if(!flag)
            {
                System.out.println(watchername+"观看了"+video);
                Thread.sleep(1000);
                this.notifyAll();
                flag=!flag;
            }else{  //否则等待,直到表演者表演完,再继续观看
                this.wait();
            }
        }
    }
    
    
    

    3、买票与卖票。

    使用加锁。保证票的数量在有的情况才可以出售,解决并发问题demo。

    package cn.xjt.gaoji;
    
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 买票系统会出现不安全
     * 需要加上锁
     * 通过ReenTrantlock对象进行加锁,解锁
     * private final ReentrantLock locks = new ReentrantLock();
     * @author xujiangtao
     * @create 2020-08-01-16:59
     */
    public class TestReentrantLock {
        public static void main(String[] args) {
            buyticket person = new buyticket();
            Thread t = new Thread(person);
            t.start();
            t=new Thread(person);
            t.start();
            t= new Thread(person);
            t.start();
        }
    
    }
    //一个卖票的类
    class buyticket implements Runnable{
        //Reentrantlock:可重进锁
        private final ReentrantLock locks = new ReentrantLock();
        //票数
        int ticket =10;
        @Override
        //重写的方法
        public void run() {
            while (true)
            {
                try {
                    //开启一个锁        开启一个锁最好放在try语句
                    locks.lock();
                    if(ticket>0)
                    {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(ticket--);
                    }
                    else
                    {
                        System.out.println("你买的票已经被购完");
                        break;
                    }
                }finally {
                    //关闭一个锁
                    locks.unlock();
                }
            }
        }
    }
    
    
    展开全文
  • 本篇文章通过代码实例给大家详细讲述了Java单线程与多线程的相关原理和知识点总结,需要的朋友可以学习下。
  • java 多线程实例

    2015-10-30 17:10:31
    java多线程应用实例,包括wait、notify、notifyAll等的应用方法,经过调试,可以直接运行。
  • Java 多线程访问的经典例子,一个个代码 手动敲出,可以加深或理解 多线程访问 线程安全与同步的问题.
  • 同步控制是并发程序必不可少的重要手段,本文我们将通过重入锁、读写锁、信号量、倒计数器和循环栅栏以及他们的实例来介绍Java并发程序中的同步控制。 目录线程安全 Thread Safety重入锁 ReentrantLock读写锁 ...
  • java多线程例子.pdf

    2021-09-30 17:29:05
    java多线程例子.pdf
  • 利用JAVA编写的多线程运行实例 压缩文件,可直接运行 ...利用JAVA编写的多线程运行实例 压缩文件,可直接运行利用JAVA编写的多线程运行实例 压缩文件,可直接运行利用JAVA编写的多线程运行实例 压缩文件,可直接运行
  • Java多线程实现方式有两种,第一种是继承Thread类,第二种是实现Runnable接口,两种有很多差异,下面跟着本文一起学习吧
  • java多线程编程实例

    万次阅读 多人点赞 2018-05-25 10:01:22
    这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下。1.相关知识:Java多线程程序设计到的知识:(1)对同一个数量...

            这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下。

    1.三个售票窗口同时出售20张票

    程序分析:
        (1)票数要使用同一个静态值
        (2)为保证不会出现卖出同一个票数,要java多线程同步锁。
    设计思路:
        (1)创建一个站台类Station,继承Thread,重写run方法,在run方法里面执行售票操作!售票要使用同步锁:即有一个站台卖这张票时,其他站台要等这张票卖完!
        (2)创建主方法调用类

    (一)创建一个站台类,继承Thread

    package com.xykj.threadStation;
    public class Station extends Thread {
        // 通过构造方法给线程名字赋值
        public Station(String name) {
           super(name);// 给线程名字赋值
        }
        // 为了保持票数的一致,票数要静态
        static int tick = 20;
        // 创建一个静态钥匙
        static Object ob = "aa";//值是任意的
        // 重写run方法,实现买票操作
        @Override
        public void run() {
          while (tick > 0) {
            synchronized (ob) {// 这个很重要,必须使用一个锁,
              // 进去的人会把钥匙拿在手上,出来后才把钥匙拿让出来
              if (tick > 0) {
                System.out.println(getName() + "卖出了第" + tick + "张票");
                tick--;
              } else {
                System.out.println("票卖完了");
              }
            }
            try {
               sleep(1000);//休息一秒
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
      }
    }

    (二)创建主方法调用类

    package com.xykj.threadStation;
    public class MainClass {
      /**
       * java多线程同步锁的使用
       * 示例:三个售票窗口同时出售10张票
       * */
      public static void main(String[] args) {
        //实例化站台对象,并为每一个站台取名字
         Station station1=new Station("窗口1");
         Station station2=new Station("窗口2");
         Station station3=new Station("窗口3");
        // 让每一个站台对象各自开始工作
         station1.start();
         station2.start();
         station3.start();
      }
    }

    程序运行结果:

    窗口1卖出了第20张票
    窗口2卖出了第19张票
    窗口3卖出了第18张票
    窗口3卖出了第17张票
    窗口1卖出了第16张票
    窗口2卖出了第15张票
    窗口3卖出了第14张票
    窗口1卖出了第13张票
    窗口2卖出了第12张票
    窗口2卖出了第11张票
    窗口1卖出了第10张票
    窗口3卖出了第9张票
    窗口3卖出了第8张票
    窗口1卖出了第7张票
    窗口2卖出了第6张票
    窗口3卖出了第5张票
    窗口1卖出了第4张票
    窗口2卖出了第3张票
    窗口3卖出了第2张票
    窗口1卖出了第1张票
    票卖完了

    可以看到票数是不会有错的!

    2.两个人AB通过一个账户A在柜台取钱和B在ATM机取钱!

    程序分析:
            钱的数量要设置成一个静态的变量,两个人要取的同一个对象值。
    (一)创建一个Bank类
    package com.thread.demo.demo2;
    
    import java.util.Objects;
    
    public class Bank {
    	// 假设一个账户有1000块钱  
    	static double money = 1000;
    	// 柜台Counter取钱的方法  
    	private void Counter(double money) {
    		Bank.money -= money;
    		System.out.println("柜台取钱" + money + "元,还剩" + Bank.money + "元!");
    	}
    	// ATM取钱的方法  
    	private void ATM(double money) {
    		Bank.money -= money;
    		System.out.println("ATM取钱" + money + "元,还剩" + Bank.money + "元!");
    	}
    	
    	//提供一个对外取款途径,防止直接调取方法同时取款时,并发余额显示错误
    	public synchronized void outMoney(double money, String mode) throws Exception{
    		if(money > Bank.money){
    			//校验余额是否充足
    			throw new Exception("取款金额"+money+",余额只剩"+Bank.money+",取款失败");
    		}
    		if(Objects.equals(mode, "ATM")){
    			ATM(money);
    		} else {
    			Counter(money);
    		}
    	}
    
    }

    (二)创建一个PersonA类

    package com.thread.demo.demo2;
    
    public class PersonA extends Thread {
    
    	Bank bank;
    	
    	String mode;
    
    	public PersonA(Bank bank, String mode) {
    		this.mode = mode;
    		this.bank = bank;
    	}
    
    	public void run (){
    		while(bank.money >= 100){
    			try {
    				bank.outMoney(100, mode);
    			} catch (Exception e1) {
    				// TODO Auto-generated catch block
    				e1.printStackTrace();
    			}
    			try {
    				sleep(100);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    }

    (三)创建一个PersonB类

    package com.thread.demo.demo2;
    
    public class PersonB extends Thread {
    	Bank bank;
    	
    	String mode;
    
    	public PersonB(Bank bank, String mode) {
    		this.bank = bank;
    		this.mode = mode;
    	}
    
    	public void run() {
    		while (bank.money >= 200) {
    			try {
    				bank.outMoney(200, mode);
    			} catch (Exception e1) {
    				// TODO Auto-generated catch block
    				e1.printStackTrace();
    			}
    			try {
    				sleep(100);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		
    	}
    }

    (四)创建主方法的调用类

    package com.thread.demo.demo2;
    
    /**
     * 两个人AB通过一个账户A在柜台取钱和B在ATM机取钱
     * */
    public class MainClass {
    	public static void main(String[] args) {
    		Bank bank = new Bank();
    		// 实例化两个人,传入同一个银行的对象
    		PersonA a = new PersonA(bank, "Counter");
    		PersonB b = new PersonB(bank, "ATM");
    		a.start();
    		b.start();
    	}
    }
    运行结果:

    可以看到取完就停止运行了。

    3.龟兔赛跑问题

    龟兔赛跑:2000米 
    要求:
        (1)兔子每 0.1 秒 5 米的速度,每跑20米休息1秒;
        (2)乌龟每 0.1 秒跑 2 米,不休息;
        (3)其中一个跑到终点后另一个不跑了!
    程序设计思路:
        (1)创建一个Animal动物类,继承Thread,编写一个running抽象方法,重写run方法,把running方法在run方法里面调用。
        (2)创建Rabbit兔子类和Tortoise乌龟类,继承动物类
        (3)两个子类重写running方法
        (4)本题的第3个要求涉及到线程回调。需要在动物类创建一个回调接口,创建一个回调对象。

    (一)创建Animal动物类

    package com.thread.demo.demo3;
    
    public abstract class Animal extends Thread {
    	public int length = 2000;// 比赛长度
    
    	public abstract void runing();
    
    	@Override
    	public void run() {
    		super.run();
    		while (length > 0) {
    			runing();
    		}
    	}
    
    	// 在需要回调数据的地方(两个子类需要),声明一个接口
    	public static interface Calltoback {
    		public void win();
    	}
    
    	// 2.创建接口对象
    	public Calltoback calltoback;
    
    }

    (二)创建Rabbit兔子类

    package com.thread.demo.demo3;
    
    public class Rabbit extends Animal {
    
    	public Rabbit() {
    		setName("兔子");
    	}
    
    	@Override
    	public void runing() {
    		//兔子速度
    		int dis = 5;
    		length -= dis;
    
    		System.out.println("兔子跑了" + dis + "米,距离终点还有" + length + "米");
    		if (length <= 0) {
    			length = 0;
    			System.out.println("兔子获得了胜利");
    			// 给回调对象赋值,让乌龟不要再跑了
    			if (calltoback != null) {
    				calltoback.win();
    			}
    		}
    
    		try {
    			if ((2000 - length) % 20 == 0) {	// 每20米休息一次,休息时间是1秒
    				sleep(1000);
    			} else {				//没0.1秒跑5米
    				sleep(100);
    			}
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    }

    (三)创建Tortoise乌龟类

    package com.thread.demo.demo3;
    
    public class Tortoise extends Animal {
    	public Tortoise() {
    		setName("乌龟");// Thread的方法,给线程赋值名字
    	}
    
    	// 重写running方法,编写乌龟的奔跑操作
    	@Override
    	public void runing() {
    		// 乌龟速度
    		int dis = 2;
    		length -= dis;
    		System.out.println("乌龟跑了" + dis + "米,距离终点还有" + length + "米");
    		if (length <= 0) {
    			length = 0;
    			System.out.println("乌龟获得了胜利");
    			// 让兔子不要在跑了
    			if (calltoback != null) {
    				calltoback.win();
    			}
    		}
    		try {
    			sleep(100);						//没0.1秒跑2米
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    }

    (四)创建一个让动物线程停止的类,这里要实现回调接口

    package com.thread.demo.demo3;
    
    import com.thread.demo.demo3.Animal.Calltoback;
    
    public class LetOneStop implements Calltoback {
    	// 动物对象
    	Animal an;
    
    	// 获取动物对象,可以传入兔子或乌龟的实例
    	public LetOneStop(Animal an) {
    		this.an = an;
    	}
    
    	// 让动物的线程停止
    	@Override
    	public void win() {
    		// 线程停止
    		an.stop();
    	}
    }

    (五)创建一个主方法调用类

    package com.thread.demo.demo3;
    
    public class MainClass {
    	/**
    	 * 龟兔赛跑:2000米
    	 */
    	public static void main(String[] args) {
    		// 实例化乌龟和兔子
    		Tortoise tortoise = new Tortoise();
    		Rabbit rabbit = new Rabbit();
    		// 回调方法的使用,谁先调用calltoback方法,另一个就不跑了
    		LetOneStop letOneStop1 = new LetOneStop(tortoise);
    		// 让兔子的回调方法里面存在乌龟对象的值,可以把乌龟stop
    		rabbit.calltoback = letOneStop1;
    		LetOneStop letOneStop2 = new LetOneStop(rabbit);
    		// 让乌龟的回调方法里面存在兔子对象的值,可以把兔子stop
    		tortoise.calltoback = letOneStop2;
    		// 开始跑
    		tortoise.start();
    		rabbit.start();
    	}
    
    }

    运行结果:


    4. 线程示例总结

        (1)代码块锁是一个防止数据发生错误的一个重要手段;

        (2)对象的统一性是非常重要的,这要想到对象的传入问题,要操作的对象只能new一次,其他的操作都是对这个传入的对象进行的,才能保证数据一致性,完整性和正确性。

    展开全文
  • 本文主要介绍java多线程-同步块的知识,这里整理了相关的详细资料及简单示例代码,有兴趣的小伙伴可以参考下
  • java多线程的常见例子

    万次阅读 多人点赞 2016-09-13 11:43:44
    本文有比较常见的java多线程问题的解决方法。对部分知识也有总结。

    java多线程的常见例子

     

     

    一.相关知识:

     

    Java多线程程序设计到的知识:

    (一)对同一个数量进行操作

    (二)对同一个对象进行操作

    (三)回调方法使用

    (四)线程同步,死锁问题

    (五)线程通信

     等等

     

     

    二.示例一:三个售票窗口同时出售20张票;

     

    程序分析:1.票数要使用同一个静态值

     2.为保证不会出现卖出同一个票数,要java多线程同步锁。

    设计思路:1.创建一个站台类Station,继承Thread,重写run方法,在run方法里面执行售票操作!售票要使用同步锁:即有一个站台卖这张票时,其他站台要等这张票卖完!

    2.创建主方法调用类

     

    (一)创建一个站台类,继承Thread

     

    
    package com.xykj.threadStation;
     
    public class Station extends Thread {
     
            // 通过构造方法给线程名字赋值
            public Station(String name) {
                 super(name);// 给线程名字赋值
            }
             
            // 为了保持票数的一致,票数要静态
            static int tick = 20;
             
            // 创建一个静态钥匙
            static Object ob = "aa";//值是任意的
             
            // 重写run方法,实现买票操作
            @Override
            public void run() {
                while (tick > 0) {
                    synchronized (ob) {// 这个很重要,必须使用一个锁,
                        // 进去的人会把钥匙拿在手上,出来后才把钥匙拿让出来
                        if (tick > 0) {
                            System.out.println(getName() + "卖出了第" + tick + "张票");
                            tick--;
                        } else {
                            System.out.println("票卖完了");
                        }
                    }
                    try {
                         sleep(1000);//休息一秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                 
                }
        }
     
    }
     
     


    (二)创建主方法调用类

     

     

    package com.xykj.threadStation;
     
    public class MainClass {
        /**
         * java多线程同步锁的使用
         * 示例:三个售票窗口同时出售10张票
         * */
        public static void main(String[] args) {
            //实例化站台对象,并为每一个站台取名字
             Station station1=new Station("窗口1");
             Station station2=new Station("窗口2");
             Station station3=new Station("窗口3");
         
            // 让每一个站台对象各自开始工作
             station1.start();
             station2.start();
             station3.start();
         
        }
     
    }

     

     

    程序运行结果:

     

     

     

     

     

    可以看到票数是不会有错的!

     

     

     

     

    三.示例二:两个人AB通过一个账户A在柜台取钱和B在ATM机取钱!

     

    程序分析:钱的数量要设置成一个静态的变量。两个人要取的同一个对象值

     

    (一)创建一个Bank类

     

     

    
     
    package com.xykj.bank;
     
    public class Bank {
     
        // 假设一个账户有1000块钱
        static int money = 1000;
         
        // 柜台Counter取钱的方法
        public void Counter(int money) {// 参数是每次取走的钱
            Bank.money -= money;//取钱后总数减少
            System.out.println("A取走了" + money + "还剩下" + (Bank.money));
        }
         
        // ATM取钱的方法
        public void ATM(int money) {// 参数是每次取走的钱
            Bank.money -= money;//取钱后总数减少
            System.out.println("B取走了" + money + "还剩下" + (Bank.money));
        }
         
    }

     

    (二)创建一个PersonA类


    package com.xykj.bank;
     
    public class PersonA extends Thread {
        // 创建银行对象
        Bank bank;
         
        // 通过构造器传入银行对象,确保两个人进入的是一个银行
        public PersonA(Bank bank) {
             this.bank = bank;
        }
        
        //重写run方法,在里面实现使用柜台取钱
        @Override
            public void run() {
                while (Bank.money >= 100) {
                    bank.Counter(100);// 每次取100块
                try {
                    sleep(100);// 取完休息0.1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

     

     

    (三)创建一个PersonB类

     

     

    
    package com.xykj.bank;
     
    public class PersonB extends Thread {
        // 创建银行对象
        Bank bank;
         
        // 通过构造器传入银行对象,确保两个人进入的是一个银行
        public PersonB(Bank bank) {
            this.bank = bank;
        }
         
        // 重写run方法,在里面实现使用柜台取钱
        @Override
        public void run() {
            while (Bank.money >= 200) {
                bank.ATM(200);// 每次取200块
                try {
                    sleep(100);// 取完休息0.1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
             
        }
    }
     

     

    (四)创建主方法的调用类

     

    package com.xykj.bank;
     
    public class MainClass {
        /**
         * 两个人AB通过一个账户A在柜台取钱和B在ATM机取钱
         * */
        public static void main(String[] args) {
            // 实力化一个银行对象
            Bank bank = new Bank();
            // 实例化两个人,传入同一个银行的对象
            PersonA pA = new PersonA(bank);
            PersonB pB = new PersonB(bank);
            // 两个人开始取钱
            pA.start();
            pB.start();
             
        }
     
    }

      

    运行结果:

     


     

     

    可以看到取完就停止运行了。

     

     

     

    四.示例三:龟兔赛跑问题

     

    龟兔赛跑:20米     //只要为了看到效果,所有距离缩短了

     要求:

    1.兔子每秒0.5米的速度,每跑2米休息10秒,

    2.乌龟每秒跑0.1米,休息 

      3.其中一个跑到终点后另一个不跑了!

           程序设计思路:

    1.创建一个Animal动物类,继承Thread,编写一个running抽象方法,重写run方法,把running方法在run方法里面调用。

    2.创建Rabbit兔子类和Tortoise乌龟类,继承动物类

    3.两个子类重写running方法

    4.本题的第3个要求涉及到线程回调。需要在动物类创建一个回调接口,创建一个回调对象

     

    (一)创建Animal动物类

     

     

    
    package com.xykj.rabbit_tortoise;
     
    public abstract class Animal extends Thread{
    
        public double length=20;//比赛的长度
        
        public abstract void runing();//抽象方法需要子类实现
        
        //在父类重写run方法,在子类只要重写running方法就可以了
        @Override
        public void run() {
            super.run();
            while (length>0) {
                 runing();
            }
        }
        
        //在需要回调数据的地方(两个子类需要),声明一个接口
        public static interface Calltoback{
            public void win();
        }
        
        //2.创建接口对象
        public Calltoback calltoback;
        
    }


    (二)创建Rabbit兔子类

     

    package com.xykj.rabbit_tortoise;
     
    public class Rabbit extends Animal {
         
        public Rabbit() {
            setName("兔子");// Thread的方法,给线程赋值名字
        }
         
        // 重写running方法,编写兔子的奔跑操作
        @Override
        public void runing() {
            // 跑的距离
            double dis = 0.5;
            length -= dis;//跑完后距离减少
            if (length <= 0) {
                length = 0;
                System.out.println("兔子获得了胜利");
                //给回调对象赋值,让乌龟不要再跑了
                if (calltoback != null) {
                    calltoback.win();
                }
            }
            System.out.println("兔子跑了" + dis + "米,距离终点还有" + (int)length + "米");
             
            if (length % 2 == 0) {// 两米休息一次
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
     

     

    (三)创建Tortoise乌龟类


     
    package com.xykj.rabbit_tortoise;
     
    public class Tortoise extends Animal {
         
        public Tortoise() {
            setName("乌龟");// Thread的方法,给线程赋值名字
        }
         
        // 重写running方法,编写乌龟的奔跑操作
        @Override
        public void runing() {
            // 跑的距离
            double dis = 0.1;
            length -= dis;
            if (length <= 0) {
                length = 0;
                System.out.println("乌龟获得了胜利");
                // 让兔子不要在跑了
                if (calltoback != null) {
                    calltoback.win();
                }
            }
            System.out.println("乌龟跑了" + dis + "米,距离终点还有" + (int) length + "米");
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


     

    (四)创建一个让动物线程停止的类,这里要实现回调接口


    package com.xykj.rabbit_tortoise;
     
    import com.xykj.rabbit_tortoise.Animal.Calltoback;
     
    public class LetOneStop implements Calltoback {
    
        // 动物对象
        Animal an;
        
        // 获取动物对象,可以传入兔子或乌龟的实例
        public LetOneStop(Animal an) {
            this.an = an;
        }
        
        //让动物的线程停止
        @Override
        public void win() {
            // 线程停止
            an.stop();
        }
         
    }
     
     
     



    (五)创建一个主方法调用类,


    package com.xykj.rabbit_tortoise;
     
    public class MainClass {
        /**
         * 龟兔赛跑:20米      
         * */
        public static void main(String[] args) {
            //实例化乌龟和兔子
            Tortoise tortoise = new Tortoise();
            Rabbit rabbit = new Rabbit();
            //回调方法的使用,谁先调用calltoback方法,另一个就不跑了
            LetOneStop letOneStop1 = new LetOneStop(tortoise);
            rabbit.calltoback = letOneStop1;//让兔子的回调方法里面存在乌龟对象的值,可以把乌龟stop
            LetOneStop letOneStop2 = new LetOneStop(rabbit);
            tortoise.calltoback = letOneStop2;//让乌龟的回调方法里面存在兔子对象的值,可以把兔子stop
            //开始跑
            tortoise.start();
            rabbit.start();
         
        }
     
    }

     

     

    运行结果:

     

     

     

    可以看到结果兔子赢了。

    一般来说兔子获得了胜利是在最后输出的,

    但是,由于线程一直在执行所以会出现:

    “兔子跑了0.5米,距离终点还有0米”还没来得及输出完,

    而“兔子获得了胜利”已经输出完毕了。

     

     

    五.实例四:

    在一个KFC内,服务员负责生产食物,消费者负责消费食物;

    当生产到一定数量可以休息一下,直到消费完食物,再马上生产,一直循环

     

    程序涉及到的内容:

    1.这设计到java模式思想:生产者消费者模式

    2.要保证操作对象的统一性,即消费者和服务者都是跟同一个KFC发生关系的,KFC只能new一次

    3.this.notifyAll();和 this.wait();一个是所有唤醒的意思,一个是让自己等待的意思;

    比如本题中,生产者生产完毕后,先所有唤醒(包括消费者和生产者),再让所有自己(生产者)等待

     这时,消费者开始消费,直到食材不够,先所有唤醒(包括消费者和生产者),再让所有自己(消费者)等待

    一直执行上面的操作的循环

    4.生产者和消费者都要继承Thread,才能实现多线程的启动

     

     

    程序设计的步骤思路:

    1.创建一个食物类Food,有存放/获取食物的名称的方法

    2.创建一个KFC类,有生产食物和消费食物的方法

    3.创建一个客户类Customer,继承Thread,重写run方法,在run方法里面进行消费食物操作

    4.创建一个服务员类Waiter,继承Thread,重写run方法,在run方法里面进行生产食物的操作

    5.创建主方法的调用类

     

     

    (一)创建一个食物类Food


    package com.xykj.producer_consumer;
     
    public class Food {
        String name="";
        //通过构造方法传入食物的名字
        public Food(String name) {
            this.name=name;
        }
        //get、set 方法
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }

     

    (二)创建一个KFC类


    package com.xykj.producer_consumer;
    import java.util.ArrayList;
    import java.util.List;
     
    public class KFC {
    
        //食物的种类
        String[] names = { "薯条", "烧板", "鸡翅", "可乐" };
        
        //生产的最大值,到达后可以休息
        static final int Max = 20;
        
        //存放食物的集合
        List
       
         foods = new ArrayList
        
         ();
         
        // 生产食物的方法
        public void prod(int index) {
            synchronized (this) {
                // 如果食物数量大于20
                while (foods.size() > Max) {
                    System.out.println("食材够了");
                    this.notifyAll();//这个唤醒是针对生产者和消费者,有all
                    try {
                        String name=Thread.currentThread().getName();
                        this.wait();//这个唤醒是针对生产者,没有all
                        System.out.println("生产者:"+name);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                 
                // 开始生产食物食物//有一点要注意的
                System.out.println("开始生产食物");
                for (int i = 0; i < index; i++) {
                    Food food = new Food(names[(int) (Math.random() * 4)]);
                    foods.add(food);
                    System.out.println("生产了" + food.getName() + foods.size());
                }
            }
        }
         
        // 消费食物的方法
        public void consu(int index) { 
            synchronized (this) {
                while (foods.size() < index) {
                    System.out.println("食材不够了");
                    this.notifyAll();//这个唤醒是针对生产者和消费者,有all
                    try {
                        String name=Thread.currentThread().getName();
                        this.wait();//这个唤醒是针对消费者,没有all
                        System.out.println("消费者:"+name);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 足够消费
                System.out.println("开始消费");
                for (int i = 0; i < index; i++) {
                    Food food = foods.remove(foods.size() - 1);
                    System.out.println("消费了一个" + food.getName() + foods.size());
                }
            }
        }
    }
     
        
       


     

    (三)创建一个客户类Customer

     

    package com.xykj.producer_consumer;
     
    public class Customers extends Thread{
        KFC kfc;
        //KFC要传入,保证每一个服务员和用户在同一个KFC对象内
        public Customers(KFC kfc) {
            this.kfc=kfc;
        }
        @Override
        public void run() {
            int size=(int)(Math.random()*5);//每次要消费的食物的数量
            while (true) {
                kfc.consu(size);//在消费的方法里面传入参数
            }
         
        }
    }
     

     

     

    (四)创建一个服务员类Waiter

     

    package com.xykj.producer_consumer;
     
    public class Waiter extends Thread{
        KFC kfc;
        //KFC要传入,保证每一个服务员和用户在同一个KFC对象内
        public Waiter(KFC kfc) {
            this.kfc=kfc;
        }
        @Override
        public void run() {
            int size=(int)(Math.random()*5)+5;//每次生产的数量
            while (true) {
                kfc.prod(size);//传入每次生产的数量
            }
         
        }
    }

     

    (五)创建主方法的调用类

     

    
    package com.xykj.producer_consumer;
     
    public class MainClass {
        /**
         * 生产者消费者模式
         *
         * */
        public static void main(String[] args) {
        
            // 只实例化一个KFC对象,保证每一个服务员和用户在同一个KFC对象内
            KFC kfc = new KFC();
            
            //实例化4个客户对象
            Customers c1 = new Customers(kfc);
            Customers c2 = new Customers(kfc);
            Customers c3 = new Customers(kfc);
            Customers c4 = new Customers(kfc);
            
            //实例化3个服务员对象
            Waiter waiter1 = new Waiter(kfc);
            Waiter waiter2 = new Waiter(kfc);
            Waiter waiter3 = new Waiter(kfc);
            
            //让所有的对象的线程都开始工作
            waiter1.start();
            waiter2.start();
            waiter3.start();
            c1.start();
            c2.start();
            c3.start();
            c4.start();
        }
         
    }
     
     

     

    六.示例五:设计四个线程对象对同一个数据进行操作,

      两个线程执行减操作,两个线程执行加操作。

     

    程序分析:1.创建一个ThreadAddSub类继承Thread,重写run方法

       2.在run方法里面实现加和减的操作,每次操作后睡眠1秒

       3.创建主方法调用类

     

    (一)创建一个ThreadAddSub类

     

    package com.xykj.add;
     
    public class ThreadAddSub extends Thread {
        //判断要进行的操作
        boolean operate = true;
        //要操作的数
        static int sum = 0;
         
        // 把操作运算通过构造方法传进来
        public ThreadAddSub(boolean operate) {
            super();
            this.operate = operate;
        }
         
        @Override
        public void run() {
            super.run();
            while (true) {
                if (operate) {
                    sum+=5;
                    System.out.println("加后,sum="+sum);
                } else {
                    sum-=4;
                    System.out.println("减后,sum="+sum);
                }
                try {
                    sleep(500);// 睡眠0.5秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
         
        }
    }

     

     (二)创建主方法调用类

     

    emptypackage com.xykj.add;
     
    public class MainClass {
        /**
         * (线程同步)
         * */
        public static void main(String[] args) {
        
            //创建一个存放ThreadAddSub对象的数组
            ThreadAddSub[] tSub=new ThreadAddSub[4];
            for (int i = 0; i < tSub.length; i++) {
            
            //把实例化ThreadAddSub对象赋值到数组内
            //第一三个是true,二四个是false
            tSub[i]=new ThreadAddSub(i%2==0?true:false);
            
            //让线程开始工作
            tSub[i].start();
            }
         
        }
     
    }

      

     

    线程示例总结:

    代码块锁是一个防止数据发生错误的一个重要手段。

    对象的统一性是非常重要的,这要想到对象的传入问题,

    要操作的对象只能new一次,其他的操作都是对这个传入的对象进行的,

    才能保证数据一致性,完整性和正确性。


    练习题目:


    1. (多线程)代码实现火车站4个卖票窗口同时买票的场景,输出示例:
    窗口1卖票
    窗口2卖票
    窗口1卖票
    ...
    2. (线程同步)代码实现火车站4个窗口同时卖100张票的代码逻辑,同一个窗口不能卖同一
    张张票。
    3. (线程通信)小明打算去提款机上取钱,发现卡上没钱,这时候他告知妈妈去存钱,妈妈
    存了钱了,告知小明存好了可以取钱了。(PS:小明分多次取钱,每次取100,当发现钱不够
    100,就等待妈妈存钱,小明他妈每次存2000,当发现钱小于100就存钱,就存钱,并且
    通知小明去取钱,当大于100就等待小明钱不够是再存)
    4. (线程同步)设计四个线程对象对同一个数据进行操作,两个线程执行减操作,两个线程执行
    加操作。
    5. (线程通信)制作两个线程对象,要求用同步块的方式使第一个线程运行2次,然后将自己
    阻塞起来,唤醒第二个线程,第二个线程再运行2次,然后将自己阻塞起来,唤醒第一个线
    程……两个线程交替执行。
    6. (线程同步)设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。
    7. (线程通信)子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着
    再回到主线程又循环100,如此循环50次。



     

    展开全文
  • Java多线程编程实战指南(核心篇) 高清pdf带目录 随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了――如今...
  • java多线程Runnable实例

    2014-01-24 21:38:58
    java多线程runnable实例,经过测试的,可以直接运行
  • 主要介绍了Java多线程回调方法实例解析,具有一定参考价值,需要的朋友可以了解下。
  • java多线程实例

    2011-12-07 20:22:28
    java多线程实例
  • Android 文件下载功能Java多线程下载功能的例子实例源码
  • 读完本文大概需要30分钟左右,本文有大量的实例代码和典型例题帮助大家理解java多线程。虽然多线程比较难以理解,仅靠一篇文章恐怕搞不定。但读完本文最起码能让你搞懂java多线程的70%以上,至于熟练运用,还是得...
  • java 多线程并发实例

    2014-03-11 13:30:52
    代码里面包含一个并发4个线程同时运行 全部开始 全部停止 单个停止还有点问题。 还有生产者消费者 里面的里面能帮助你理解多线程的运用!
  • 介绍了JAVA实现多线程的两种方法实例分享,有需要的朋友可以参考一下
  • 使用Java多线程实现管道过滤器,本文档写了六个线程,五个管道,内容很简单,如果想要复杂功能请手动添加。
  • java多线程并发编程例子

    热门讨论 2012-03-08 12:11:38
    关于java.util.concurrent多线程核心包内各种线程资源的使用场景例子
  • java多线程示例

    2019-08-04 01:31:54
    NULL 博文链接:https://892530175-qq-com.iteye.com/blog/2030207
  • JAVA多线程实例

    2013-04-17 23:02:12
    描述java多线程例子,可供进一步学习多线程的人参考

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 747,369
精华内容 298,947
关键字:

java多线程实例