美文网首页
java多线程--线程安全与同步

java多线程--线程安全与同步

作者: bigpeng个人博客 | 来源:发表于2018-10-16 13:13 被阅读13次

    1、什么是线程安全?
    我们通常会说HashMap 和HashTable的区别是一个是线程不安全的,一个是线程安全的。同样的StringBuilder和StringBuffer中前者是线程不安全的,后者是线程安全的。那么什么是线程安全呢?
    所谓线程安全,即不管多少个线程同时执行,代码的结果跟单线程执行的结果总会是一致的,那么我们就说是线程安全的。反之则为线程不安全。
    线程安全问题一般都是由全局变量及静态变量引起的。
    2、线程安全问题示例代码(购票)
    示例代码:

    /**
     * 购票线程,线程不安全
     */
    public class Ticket implements Runnable {
        private int ticketNum=100;
        @Override
        public void run() {
    
            while(true){
                if(ticketNum>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":购买了第"+ticketNum+"张票");
                    ticketNum--;
                }else{
                    break;
                }
            }
        }
    }
    
    public static void ticketTest(){
            Ticket ticketTread = new Ticket();
            for (int i = 0; i <5 ; i++) {
                Thread t1=new Thread(ticketTread);
                t1.setName("曹操家小兵"+i);
                t1.start();
            }
        }
    

    3、线程安全的解决办法
    1)加同步锁synchronized
    synchronized关键字为同步锁关键字,可以用于对象锁和类锁,方法锁。

    2)加java锁对象Lock
    创建Lock对象

    示例代码:

    /**
     * 购票类
     * 通过synchoronized 实现线程同步
     */
    public class TicketSync implements Runnable {
        private  Integer ticketNum = 100;
        Object obj=new Object();
    
        @Override
        public void run() {
            while (true) {
                try {
                    synchronized (obj) {
                        if (ticketNum > 0) {
    
                            Thread.sleep(100);
    
                            System.out.println(Thread.currentThread().getName() + ":购买了第" + (ticketNum--) + "票");
                        } else {
                            break;
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    public class TicketLock implements Runnable {
        private int ticketNum = 100;
        Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
    
            while (true) {
                //取得锁
                lock.lock();
                try {
                    if (ticketNum > 0) {
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName() + ":购买了第" + ticketNum-- + "张票");
                    } else {
                        break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放锁
                    lock.unlock();
                }
            }
    
        }
    }
    
    

    4、死锁

    死锁产生的原因:线程间资源交叉使用,线程互相等待对方释放锁资源,则会产生死锁。
    上述图中有产生死锁的四个原因:

    1.互斥条件:一个资源每次只能被一个线程使用。图上每条路上只能让一个方向的汽车通过,故满足产生死锁的条件之一

    2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。可以看出,图上每个方向的汽车都在等待其他方向的汽车撤走,故满足产生死锁的条件之二

    3.不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。这里假设没有交警,那么没有人能强行要求其他方向上的汽车撤离,故满足产生死锁的条件之三

    4.循环等待条件:若干进程或线程之间形成一种头尾相接的循环等待资源关系。这个在图中很直观地表达出来了

    /**
     * 死锁演示类
     *
     */
    public class DeadLockLeft implements Runnable {
        Object left;
        Object right;
    
        public DeadLockLeft(){
    
        }
        public DeadLockLeft(Object left, Object right){
            this.left=left;
            this.right=right;
        }
        @Override
        public void run() {
    
            synchronized (left){
                System.out.println(Thread.currentThread().getName()+":报告!报告!我已进入狭窄路段左侧!");
                System.out.println(Thread.currentThread().getName()+":前方通行缓慢,我要等待一秒");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":终于快过去了,我靠,前方出现一个胖子挡我道啊!我得等他让路");
                synchronized (right){
                    System.out.println(Thread.currentThread().getName()+":那傻叉终于让路了,我可以过去啦");
                }
    
            }
    
        }
    }
    
    
    public class DeadLockRight implements Runnable {
        Object left;
        Object right;
    
        public DeadLockRight(){
    
        }
        public DeadLockRight(Object left, Object right){
            this.left=left;
            this.right=right;
        }
        @Override
        public void run() {
    
            synchronized (right){
                System.out.println(Thread.currentThread().getName()+":报告!报告!我已进入狭窄路段右侧!");
                System.out.println(Thread.currentThread().getName()+":前方通行缓慢,我要等待一秒");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":一秒等待结束,前方出现一个黑涩会大佬挡我道啊!我得等他让路");
                synchronized (left){
                    System.out.println(Thread.currentThread().getName()+":那傻叉终于让路了,我可以过去啦");
                }
    
            }
    
        }
    }
    
    /**
     * 死锁测试类
     */
    public class DeadLockTest {
        public static void main(String[] args) {
            Object left=new Object();
            Object right=new Object();
            Thread leftThread=new Thread(new DeadLockLeft(left,right));
            Thread rightThread=new Thread(new DeadLockRight(left,right));
            leftThread.setName("王雄");
            rightThread.setName("孔锦平");
            leftThread.start();
            rightThread.start();
        }
    }
    

    5、如果避免死锁
    1)只在必要的最短时间内持有锁,考虑使用同步语句块代替整个同步方法;

    2)尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁的时间尽量短暂;

    3)创建和使用一个大锁来代替若干小锁,并把这个锁用于互斥,而不是用作单个对象的对象级别锁;

    6、线程间通讯
    1)共享内存机制
    a)通过synchronized同步方式共享内存对象,来达到信息交换的目的
    b)通过wait/notify机制来实现线程间的通讯
    2)管道通信机制
    使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信。

    public class Producer extends Thread {
        Product product;
    
        public Producer(){}
        public Producer(Product product){
            this.product=product;
        }
    
        @Override
        public void run() {
            while (true){
                synchronized (product) {
                    if (product.getNumber() < 5) {
                        System.out.println(getName() + ":生产了一个" + product.getName());
                        product.setNumber(product.getNumber() + 1);
                    } else {
    
                           product.notify();
                        try {
                            product.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    //                       product.notify();
    
    
    
                    }
                }
            }
        }
    }
    
    public class Consumer extends Thread {
        private Product product;
    
        public Consumer(){}
        public Consumer(Product product){
            this.product=product;
        }
    
        @Override
        public void run() {
            while (true) {
                synchronized (product) {
    
                    if (product.getNumber() > 0) {
                        System.out.println(getName()+":来一个"+product.getName());
                        product.setNumber(product.getNumber()-1);
                    }else{
                        try {
                            product.notify();
                            product.wait();
    //                        product.notify();
    
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    
    
    /**
     * 产品类
     */
    public class Product implements Serializable{
        private static final long serialVersionUID = -6049994461715823463L;
        private String name;
        private int number;
    
        public Product() {
        }
    
        public Product(String name, int number) {
            this.name = name;
            this.number = number;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getNumber() {
            return number;
        }
    
        public void setNumber(int number) {
            this.number = number;
        }
    
        @Override
        public String toString() {
            return "Product{" +
                    "name='" + name + '\'' +
                    ", number=" + number +
                    '}';
        }
    }
    
    

    相关文章

      网友评论

          本文标题:java多线程--线程安全与同步

          本文链接:https://www.haomeiwen.com/subject/cualmftx.html