zoukankan      html  css  js  c++  java
  • 多线程(线程间的数据共享、线程安全、线程通信)

    1、线程间的数据共享

    (1)利用继承Thread类的方法实现模拟售票系统:

    public class MyThread extends Thread{       //继承自Thread类
    
        private int tickets=10;
        public void run() {//重写run方法
            while(true){
                if(tickets>0)
                    System.out.println(getName()+"售票窗口,售卖了"+tickets--+"号票");

    else
    System.exit(0);

    
            }
        }
    
    
    }
    public class Test {
    public static void main(String[] args) {
        MyThread window1 = new MyThread();
        MyThread window2 = new MyThread();
        MyThread window3 = new MyThread();
        window1.start();
        window2.start();
        window3.start();
        
    }
    }

    由运行结果可以看出,一张票在每一个窗口买了一次,在三个窗口卖了三次。因为在测试类中创建了三个线程对象,每个对象有各自的属性,他们处理的数据是相互独立的。因此,用这种方法处理售票系统是不成立的。

    (2)实现Runnable接口,模拟售票系统:

    package pers.zhb.runnable;
    
    public class MyThread implements Runnable {
        private int tickets = 10;
    
        public void run() {
            while (true) {
                if (tickets > 0)
                    System.out.println(Thread.currentThread().getName()
                            + "售票窗口,售卖了" + tickets-- + "号票");

    else
    System.exit(0);

    
            }
    
        }
    
    }
    package pers.zhb.runnable;
    
    public class RunnableDemo {
        public static void main(String[] args) throws InterruptedException {
            MyThread mt=new MyThread();
            Thread thread1 = new Thread(mt);
            Thread thread2 = new Thread(mt);
            thread1.start();
            thread2.start();
    
        }
    }

    此方法建立了三个线程,虽然他们是来自同一个对象的,即每一个线程调用的是同一个run方法,有相同的数据,但是依旧存在线程安全问题。

    2、线程安全

    (1)概念:

    单线程相当于一个人做一件事,而多线程程序中,当需要多个线程共享同一个数据时,一个线程对共享的数据进行修改时,在他没有完成相关操作之前不允许其他线程进行相关操作,否则会出现线程安全问题。当对存在安全隐患的数据加锁后,会导致程序的性能降低,所以,如果是多个线程操作同一个数据,一定要选择线程安全的方法,如果不是,可以选择线程不安全的方式来处理问题。

    (2)线程安全问题:

    package pers.zhb.runnable;
    
    public class MyThread implements Runnable {
        private int tickets = 10;
    
        public void run() {
            while (true) {
                if (tickets > 0)
                    System.out.println(Thread.currentThread().getName()
                            + "售票窗口,售卖了" + tickets-- + "号票");
                else
                    System.exit(0);
            }
    
        }
    
    }
    package pers.zhb.runnable;
    
    public class RunnableDemo {
        public static void main(String[] args) {
            MyThread mt=new MyThread();
            Thread thread1 = new Thread(mt);
            Thread thread2 = new Thread(mt);
            Thread thread3 = new Thread(mt);
            Thread thread4 = new Thread(mt);
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
    
        }
    }

    可以看出,10号票在被1号窗口卖出后,又被2号窗口卖了一次,显然这是不符合常理的。产生的原因是这样的:2号线程开始执行,tickets为10,进行了 if (tickets > 0);语句的执行,可以继续往下执行了,但是他没有抢夺到CPU资源,接着是1号线程读取数据10,进行判断可以执行,1号线程执行完后,2号线程依旧没有抢夺到CPU资源,只能继续等待。接着是3号线程、0号线程进行相关操作。当2号线程抢夺到CPU资源时,他的数据还是10,就造成了有重复票的情况。

    (3)添加同步语句:

    package pers.zhb.runnable;
    
    public class MyThread implements Runnable {
        private int tickets = 10;
        Object lock = new Object();
    
        public void run() {
    
            synchronized (lock) {
                while (true) {
                    if (tickets > 0)
                        System.out.println(Thread.currentThread().getName()
                                + "售票窗口,售卖了" + tickets-- + "号票");
                    else
                        System.exit(0);
                }
            }
    
        }
    
    }
    package pers.zhb.runnable;
    
    public class RunnableDemo {
        public static void main(String[] args) {
            MyThread mt=new MyThread();
            Thread thread1 = new Thread(mt);
            Thread thread2 = new Thread(mt);
            Thread thread3 = new Thread(mt);
            Thread thread4 = new Thread(mt);
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
    
        }
    }

    (4)创建同步方法:

    package pers.zhb.runnable;
    
    public class MyThread implements Runnable {
        private int tickets = 10;
        Object lock = new Object();
    
        public void run() {
            while (true)
                lock();
    
        }
    
        public synchronized void lock() {
            while (true) {
                if (tickets > 0)
                    System.out.println(Thread.currentThread().getName()
                            + "售票窗口,售卖了" + tickets-- + "号票");
                else
                    System.exit(0);
            }
        }
    }
    package pers.zhb.runnable;
    
    public class RunnableDemo {
        public static void main(String[] args) {
            MyThread mt=new MyThread();
            Thread thread1 = new Thread(mt);
            Thread thread2 = new Thread(mt);
            Thread thread3 = new Thread(mt);
            Thread thread4 = new Thread(mt);
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
    
        }
    }

    (5) ReentrantLock (重入锁):

    可以对资源重复加锁:

    package pers.zhb.runnable;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class MyThread implements Runnable {
        private int tickets = 10;
    
        ReentrantLock lock = new ReentrantLock();
    
        public void run() {
    
            while (true) {
                lock.lock();
                if (tickets > 0)
                    System.out.println(Thread.currentThread().getName()
                            + "售票窗口,售卖了" + tickets-- + "号票");
    
                else
                    System.exit(0);
    
            }
    
        }
    
    }
    package pers.zhb.runnable;
    
    public class RunnableDemo {
        public static void main(String[] args) {
            MyThread mt=new MyThread();
            Thread thread1 = new Thread(mt);
            Thread thread2 = new Thread(mt);
            Thread thread3 = new Thread(mt);
            Thread thread4 = new Thread(mt);
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
    
        }
    }

    3、线程之间的通信

    (1)常用方法

    wait()

    将正在执行的线程停止,并存放到线程池,直到被唤醒或等待时间到

    属于Object类

    wait用于线程间通信,这个方法会使当前拥有该对象锁的进程等待,当调用wait()方法后,线程会释放掉他所占用的锁

    sleep() 

    是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行,除非"醒来"的线程具有更高的优先级 或正在运行

    的线程因为其它原因而阻塞

    sleep()方法是Thread类的静态方法

    sleep是线程用来控制自身流程的,不涉及线程间的通信,调用sleep()方法并不会释放锁。

    notify()

    唤醒在线程池中等待的线程

    notifyAll()

    唤醒全部

    (2)举例:

    package pers.Thread.conn;
    
    public class Tickets {
        private int size;
        private int num = 0;
        public Tickets(int size){
            this.size=size;
        }
        public int getSize() {
            return size;
        }
    
        public void setSize(int size) {
            this.size = size;
        }
    
        
        public int getNum() {
            return num;
        }
    
        public void setNum(int num) {
            this.num = num;
        }
    
        boolean flag = true;
    
        public synchronized void put() {
            if (flag) //还有剩余,等待
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                num++;//等待结束后开始执行,增加票的操作
                System.out.println("存入第" + num + "号票");
                flag = true;//加票之后,有票,进入等待状态
                notify();//唤醒等待的线程
            
        }
        public synchronized void sell() {
            if (!flag) //没有剩余,等待
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }if(num>=1)
                System.out.println("卖出第" + num + "号票");
                flag = false;//减票之后,没有票,进入等待状态
                notify();//唤醒另外一个等待的线程
                if(num==size)num=size+1;
            }
        
    
    }
    package pers.Thread.conn;
    
    public class Add extends Thread{
    Tickets t=null;
    public  Add(Tickets t){//为了保证卖出和增加的为统一数据对象
        this.t=t;
    }
    public void run(){
        while(t.getNum()<t.getSize())
            t.put();
    }
    }
    package pers.Thread.conn;
    
    public class Sell extends Thread{
        Tickets t=null;
        public  Sell(Tickets t){//为了保证卖出和增加的为统一数据对象
            this.t=t;
        }
        public void run(){
            while(t.getNum()<=t.getSize())
                t.sell();
        }
    }
    package pers.Thread.conn;
    
    public class Test {
    public static void main(String[] args) {
        Tickets t=new Tickets(15);
        new Add(t).start();//卖出和增加调用的为同一个对象
        new Sell(t).start();
        
    }
    }
  • 相关阅读:
    python简介
    Error:unknown filesystem
    Ubuntu 12.04 相关问题
    C/C++版数据结构之链表<一>
    C/C++版数据结构之链表<二>
    C/C++版数据结构之树<二>
    C/C++版数据结构之树<一>
    C/C++版数据结构之链表<三>
    C/C++版数据结构之排序算法
    php 操作文件简单例子
  • 原文地址:https://www.cnblogs.com/zhai1997/p/11370572.html
Copyright © 2011-2022 走看看