如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。实现Runnable接口或callable接口,适合多个相同或不同的程序代码的线程去共享同一个资源。

多个线程共享数据分两种情况:

1、如果多个线程执行同一个Runnable实现类中的代码,此时共享的数据放在Runnable实现类中;

2、如果多个线程执行不同的Runnable实现类中的代码,此时共享数据和操作共享数据的方法封装到一个对象中,在不同的Runnable实现类中调用操作共享数据的方法。

一、 相同程序代码的多个线程共享一个资源

如果有多个线程在同时运行同一段段代码,可以使用同一个Runnable实现类,多个线程可以共享一个实现类对象,共享数据作为这个Runnable实现类的全局变量

卖票案例:

public class Demo08 {
    public static void main(String[] args) {
        //创建线程任务对象

        Ticket ticket = new Ticket();
        //创建三个窗口对象
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket, "窗口2");
        Thread t3 = new Thread(ticket, "窗口3");
        //卖票
        t1.start();
        t2.start();
        t3.start();
    }
    static class Ticket implements Runnable {
        //Object lock = new Object();
        ReentrantLock lock = new ReentrantLock();
        private int ticket = 10;
        public void run() {
            String name = Thread.currentThread().getName();
            while (true) {
                sell(name);
                if (ticket <= 0) {
                    break;
                }
            }
        }
        private synchronized void sell(String name) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }if (ticket > 0) {
                System.out.println(name + "卖票:" + ticket);
                ticket--;
            }
        }
    }
}

ticket就是全局变量,作为三个线程的共享数据。

二、 不同程序代码的多个线程共享一个资源

如果每个线程执行的代码不同,将共享数据和操作共享数据的方法封装在一个对象中,在不同的Runnable实现类调用操作共享数据的方法。

银行转账案例:

创建一个对象,对象中有操作共享数据的方法

public class Bank {

    private volatile int count =0;//账户余额

    //存钱
    public  void addMoney(int money){
        count +=money;
        System.out.println(System.currentTimeMillis()+"存进:"+money);
    }

    //取钱
    public  void subMoney(int money){
        if(count-money < 0){
            System.out.println("余额不足");
            return;
        }
        count -=money;
        System.out.println(+System.currentTimeMillis()+"取出:"+money);
    }

    //查询
    public void lookMoney(){
        System.out.println("账户余额:"+count);
    }
}

创建两个线程,线程中分别调用共享数据所在对象中不同的方法,

public class SyncThreadTest {

    public static void main(String args[]){
        final Bank bank=new Bank();

        Thread tadd=new Thread(new Runnable() {

            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    bank.addMoney(100);
                    bank.lookMoney();
                    System.out.println("\n");

                }
            }
        });

        Thread tsub = new Thread(new Runnable() {

            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    bank.subMoney(100);
                    bank.lookMoney();
                    System.out.println("\n");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
        tsub.start();
        tadd.start();
    }
}

如果一个类继承Thread,则不适合资源共享。并不是不可以实现资源共享

生产者与消费者问题

包子铺线程生产包子,吃货线程消费包子。当包子没有时(包子状态为false),吃货线程等待,包子铺线程生产包子(即包子状态为true),并通知吃货线程(解除吃货的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。接下来,吃货线程能否进一步执行则取决于锁的获取情况。如果吃货获取到锁,那么就执行吃包子动作,包子吃完(包子状态为false),并通知包子铺线程(解除包子铺的等待状态),吃货线程进入等待。包子铺线程能否进一步执行则取决于锁的获取情况。

包子资源类:
吃货线程类:
包子铺线程类:

 

实现Runnable接口比继承Thread类所具有的优势:

1. 适合多个相同的程序代码的线程去共享同一个资源。

2. 可以避免java中的单继承的局限性。

3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立

4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

 
posted on 2021-02-25 17:53  周文豪  阅读(4331)  评论(0编辑  收藏  举报