美文网首页
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 +
                '}';
    }
}

相关文章

  • 5月份第一周学习安排

    学习内容: java多线程及线程同步的方法(使用) java多线程各种同步方法的原理和优缺点 java多线程设计模...

  • Java之同步代码块

    Java多线程的同步代码块 synchronized(对象){ 需要同步的代码 } 同步代码块可以解决安全...

  • Android中的多线程

    1. Java多线程基础 Java多线程,线程同步,线程通讯 2. Android常用线程 HandlerThre...

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

    1、什么是线程安全?我们通常会说HashMap 和HashTable的区别是一个是线程不安全的,一个是线程安全的。...

  • 7.5java线程深度解析:线程的同步

    线程的同步是保证多线程安全访问竞争资源的一种手段。线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞...

  • Java 线程安全

    参考《深入了解Java虚拟机,JVM高级特性与最佳实践》 线程安全的实现方法 互斥同步: 指再多线程访问贡献数据...

  • 多线程--同步与锁

    同步与锁 上一篇中,笔者介绍了Java多线程的基础知识,主要讲解了进程/线程的区别、Java多线程的创建、Java...

  • 第十六章、synchronized和ReentrantLock

    Java在编写多线程程序时,为了保证线程安全,需要对数据同步,经常用到两种同步方式就是Synchronized和重...

  • Android下多线程的实现

    Android下多线程相关 线程安全相关问题参考:java内存模型与线程 android下与多线程有关的主要有以下...

  • Java多线程干货系列—(二)synchronized

    前言 本篇主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非...

网友评论

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

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