线程安全解决与并发包的问题,这些你都知道吗???

synchronized关键字【重点】

多行代码的原子性问题

买票案例:
TicketTask.java

//卖票任务
public class TicketTask implements Runnable {
    int count = 100;//票数

    @Override
    public void run() {
        while (true) {
            if (count > 0) {
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("卖出第" + count + "张票");
                count--;
            } else {
                break;
            }
        }
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        TicketTask ticketTask = new TicketTask();
        Thread thread1 = new Thread();
        Thread thread2 = new Thread();
        Thread thread3 = new Thread();
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

此时会出现以下情况
数据重复:当一个线程执行完卖票后,还没来得及对票数-1,被其他线程抢走CPU,导致出现重复数据。
数据异常(出现-1这种非法数据):当剩下最后一张票时,多个线程都经过了if判断,进去卖票的代码块中,然后依次卖出第1张、第0张、第-1张。

synchronized介绍

作用是可以让多句代码具有原子性(当某个线程执行多句代码过程中不被其他线程打断)。

解决方案1——同步代码块

格式:

synchronized(锁对象){
需要同步的代码(需要保持原子性的代码)
}

代码实现:

TicketTask.java

//卖票任务
public class TicketTask implements Runnable {
    int count = 100;//票数
    Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            //同步代码块
            synchronized (obj) {

            }
            if (count > 0) {
                System.out.println("卖出第" + count + "张票");
                count--;
            }
        }
    }
}

解决方案2——同步方法

格式:

publicsynchronized void 方法名{
可能会产生线程安全问题的代码}

代码实现:

Ticket.java

//卖票任务
public class TicketTask implements Runnable {
    int count = 100;//票数

    @Override
    public void run() {
        while (true) {
            sellTicket();
        }
    }

    public synchronized void sellTicket() {
        if (count > 0) {

            System.out.println("卖出第" + count + "张票");
            count--;
        }
    }
}

注意:
1、同步代码块和同步方法原理差不多,同步代码块的同步锁需要我们自己指定,而同步方法的同步锁,默认使用当前对象this。
2、如果同步方法是static,默认使用当前类的字节码文件作为锁对象。

解决方案3——Lock锁

Lock是一个接口,我们需要使用其实现类ReentrantLock。
方法:

public ReentrantLock();
public void lock();
public void unlock();

格式:

ReentrantLock lock=new ReentrantLock();//创建一个锁对象
lock.lock();//加锁
lock.unlock();//解锁

代码实现:

Ticket.java

//卖票任务
public class TicketTask implements Runnable {
    int count = 100;//票数
    //创建一个lock锁
    ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();//加锁
            if (count > 0) {

                System.out.println("卖出第" + count + "张票");
                count--;
            }
            lock.unlock();//解锁
        }
    }
}

并发包

什么是并发包?是指在JDK的并发包里提供了包含一个在公并发情况使用集合或工具类,使用这些集合或工具类时,能保证高并发情况下是安全的。

1、CopyOnWriteArrayList

ArrayList是不安的
代码实现:
MyThread.java

//这样最终结果会抛出异常
public class MyThread extends Thread {
    ArrayList arrayList = new ArrayList();

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            arrayList.add(i);
        }
        System.out.println("添加完毕");
    }
}

使用CopyOnWriteArrayList可以保证线程安全
代码实现:

MyThread.java

public class MyThread extends Thread {
    //ArrayList arrayList = new ArrayList();
    public static List<Integer> list = new CopyOnWriteArrayList<>();

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
        System.out.println("添加完毕");
    }
}

2、CopyOnWriteArraySet

代码实现:

MyThread.java

public class MyThread extends Thread {
    //ArrayList arrayList = new ArrayList();
    //public static List<Integer> list = new CopyOnWriteArrayList<>();
    public static Set<Integer> set = new CopyOnWriteArraySet<>();

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            set.add(i);
        }
        System.out.println("添加完毕");
    }
}

3、ConcurrentHashMap

代码实现:

MyThread.java

public class MyThread extends Thread {
    //ArrayList arrayList = new ArrayList();
    //public static List<Integer> list = new CopyOnWriteArrayList<>();
    //public static Set<Integer> set = new CopyOnWriteArraySet<>();
    public static Map<Integer, Integer> map = new ConcurrentHashMap<>();

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            map.put(i, i);
        }
        System.out.println("添加完毕");
    }
}

4、CountDownLatch

作用:允许一个线程,等待其他线程完成某种操作后,当前线程执行。
构造方法:

public CountDownLatch(int count);//需要传入计数器,需要等待的线程收

成员方法:

public void await throws InterruptedException;//让当前线程等待
public void countDown();//计数器-1

代码实现:

MyThread.java

5、CyclicBarrier

作用:让多个线程都到达了某种要求之后,新的任务才嫩执行。
构造方法:

public CyclicBarrier(int pattise,Runnable barrierAction);//需要多少个线程,所有线程都满足要求了,执行的任务

成员方法:

public int await ();

6、Semaphore

构造方法:

public Semaphore(int permits);//参数permits表示最多允许有多少个线程并发执行。

成员方法:

 public void semaphore.acquire();//获取线程的许可证
 public void semaphore.release();//释放线程的许可证

代码实现:

MyThread.java

public class MyThread extends Thread {
    private Semaphore semaphore;

    public MyThread(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        //从Semaphore获取线程许可
        try {
            Thread.sleep(1000);
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "线程执行了" + System.currentTimeMillis());
        //归还线程的许可
        semaphore.release();
    }
}

7、Exchanger

作用:交换者,用于线程间的数据交换。
构造方法:

public Exchanger<V>();

成员方法:

public V exchange(V x);//参数为发给别的线程的数据,返回值别的线程发过来的数据。

代码实现:

Thread1.java

public class Thread1 extends Thread {
    private Exchanger<String> exchanger;

    public Thread1(Exchanger<String> exchanger) {
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        System.out.println("线程1将AAA发送给线程2");
        String result = null;

        try {
            result = exchanger.exchange("AAA");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程1,获取到线程2送的礼物"+result);
    }
}

Thread2.java

public class Thread2 extends Thread {
    private Exchanger<String> exchanger;

    public Thread2(Exchanger<String> exchanger) {
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        System.out.println("线程2,要将BBB,送给线程1");
        String result = null;
        try {
            result = exchanger.exchange("BBB");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程2,获取到线程1的礼物" + result);
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        //创建线程间数据交换
        Exchanger<String> exchanger = new Exchanger<>();
        //创建线程1
        Thread1 thread1 = new Thread1(exchanger);
        thread1.start();
        Thread2 thread2 = new Thread2(exchanger);
        thread2.start();
    }
}

小结

当我回头时,你就站在原地!!!
在这里插入图片描述

发布了34 篇原创文章 · 获赞 9 · 访问量 1287

猜你喜欢

转载自blog.csdn.net/weixin_43616750/article/details/104891449