【Java多线程JUC入门详解01】:Lock锁、集合的线程安全问题、生产者消费者问题synchronized锁Lock锁Lock的生产者和消费者问题关于锁的问题集合的线程安全问题Callable实现多线程

您所在的位置:网站首页 lock锁的使用步骤 【Java多线程JUC入门详解01】:Lock锁、集合的线程安全问题、生产者消费者问题synchronized锁Lock锁Lock的生产者和消费者问题关于锁的问题集合的线程安全问题Callable实现多线程

【Java多线程JUC入门详解01】:Lock锁、集合的线程安全问题、生产者消费者问题synchronized锁Lock锁Lock的生产者和消费者问题关于锁的问题集合的线程安全问题Callable实现多线程

2023-03-19 07:16| 来源: 网络整理| 查看: 265

文章目录 synchronized锁 Lock锁 与synchronized的区别 Lock的生产者和消费者问题 全部唤醒 唤醒指定线程 关于锁的问题 锁的是谁 如果锁修饰静态方法 集合的线程安全问题 CopyOnWriteArrayList CopyOnWriteArraySet ConcurrentHashMap Callable实现多线程

JUC: java.util .concurrent工具包的简称:本篇即为此工具类的入门使用博客

公平锁:先来后到

非公平锁:会根据运行时间、级别进行分配

synchronized锁 package org.example; /** * @author sshdg */ public class SynchronizedDemo { public static void main(String[] args) { SaleTicket saleTicket = new SaleTicket(); new Thread(()->{ for (int i = 0; i < 50; i++) { saleTicket.sale(); } }, "A").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { saleTicket.sale(); } }, "B").start(); } } class SaleTicket { private int number = 100; public synchronized void sale(){ System.out.println(Thread.currentThread().getName()+"卖出第"+(number--)+"张票,还剩"+number); } } Lock锁

Lock是juc包下的锁,与synchronized同步锁有一些区别,功能更加强大

package org.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Author: sshdg * @Date: 2020/9/4 20:03 */ public class LockDemo { public static void main(String[] args) { SaleTicket2 saleTicket = new SaleTicket2(); new Thread(()->{ for (int i = 0; i < 50; i++) { saleTicket.sale(); } }, "A").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { saleTicket.sale(); } }, "B").start(); } } class SaleTicket2 { private int number = 100; Lock lock = new ReentrantLock(); public void sale() { lock.lock(); try { System.out.println(Thread.currentThread().getName()+"卖出第"+(number--)+"张票,还剩"+number); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } 与synchronized的区别 synchronized java关键字 ; Lock是一个类 synchronized 无法判断获取锁的状态; Lock 可以判断是否获取到了锁 synchronized 会自动释放锁; Lock必须手动释放锁,否则会死锁 synchronized 线程1(获得锁,阻塞) - 线程2(等待,一直等); Lock 不一定会一直等待下去 synchronized 可重入锁,不可以中断,非公平锁; Lock 可重入锁,可以判断锁,非公平(默认),可以自己设置 synchronized 适合少量代码的同步问题 ; Lock 适合锁大量代码的同步问题 Lock的生产者和消费者问题 全部唤醒 package org.example.pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Author: sshdg * @Date: 2020/9/4 21:22 */ public class ProducerConsumer { public static void main(String[] args) { Date date = new Date(); new Thread(()->{ for (int i = 0; i < 10; i++) { date.increment(); } }, "A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { date.decrement(); } }, "B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { date.increment(); } }, "C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { date.decrement(); } }, "D").start(); } } class Date { private int number = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); /** * 生产 */ public void increment(){ lock.lock(); try { while (number > 0){ //等待 condition.await(); } number++; System.out.println(Thread.currentThread().getName()+":number->"+number); //通知其他线程生产完毕 condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } /** * 消费 */ public void decrement(){ lock.lock(); try { while (number == 0){ //等待 condition.await(); } number--; System.out.println(Thread.currentThread().getName()+":number->"+number); //通知其他线程消费完毕 condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } 唤醒指定线程 package org.example.pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Author: sshdg * @Date: 2020/9/5 11:12 */ public class A { public static void main(String[] args) { Date2 date2 = new Date2(); new Thread(()->{ for (int i = 0; i < 10; i++) { date2.printA(); } }, "A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { date2.printB(); } }, "B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { date2.printC(); } }, "C").start(); } } class Date2 { private int number = 1; Lock lock = new ReentrantLock(); Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); Condition condition3 = lock.newCondition(); public void printA(){ lock.lock(); try { while (number != 1){ //等待 condition1.await(); } number = 2; System.out.println(Thread.currentThread().getName()+"--> AA"); condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB(){ lock.lock(); try { while (number != 2){ //等待 condition2.await(); } number = 3; System.out.println(Thread.currentThread().getName()+"--> BB"); condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC(){ lock.lock(); try { while (number != 3){ //等待 condition3.await(); } number = 1; System.out.println(Thread.currentThread().getName()+"--> CC"); condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } 关于锁的问题 锁的是谁

锁的是对象,方法的调用者。一个对象一把锁。

synchronized

为例,锁只有一把,谁(线程)先抢到,谁先执行

package org.example.lock8; import java.util.concurrent.TimeUnit; /** * @Author: sshdg * @Date: 2020/9/5 15:24 */ public class Test1 { public static void main(String[] args) { Phone phone = new Phone(); //以这个为例,大多数情况下是A线程先抢到锁,但是这里将其睡眠1s,所以B会先执行 new Thread(()->{ try { TimeUnit.SECONDS.sleep(1); //TimeUnit.MICROSECONDS.sleep(1);睡 1微秒 也是一样,因为计算机的速度非常快 } catch (InterruptedException e) { e.printStackTrace(); } phone.sendSMS(); }, "A").start(); new Thread(()->{ phone.call(); }, "B").start(); } } class Phone{ public synchronized void sendSMS(){ System.out.println("发短信"); } public synchronized void call(){ System.out.println("打电话"); } }

而如果让方法睡1秒,则先抢到的要1s后才会执行完,在此期间占用此锁,其他线程无法执行带锁的方法。但是对无锁的方法没有影响

如果锁修饰静态方法

不建议通过对象实例访问静态方法,这样写仅为测试锁

静态方法只有一个,是在类加载的时候就有的

因此如下这种情况下,还是会先睡4s后打印发短信,然后执行打电话。

package org.example.lock8; import java.util.concurrent.TimeUnit; /** * @Author: sshdg * @Date: 2020/9/5 15:24 */ public class Test1 { public static void main(String[] args) { Phone phone = new Phone(); Phone phone2 = new Phone(); new Thread(()->{ phone.sendSMS(); }, "A").start(); new Thread(()->{ phone2.call(); }, "B").start(); } } class Phone{ public static synchronized void sendSMS(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public static synchronized void call(){ System.out.println("打电话"); } }

另外:静态方法的锁和普通方法的锁不是同一个。

集合的线程安全问题 CopyOnWriteArrayList package org.example.unsafe; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * @Author: sshdg * @Date: 2020/9/5 19:52 */ public class ListTest { public static void main(String[] args) { List list = new ArrayList(); for (int i = 1; i { list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); }, Integer.toString(i)).start(); } } }

如上代码,很容易会出现

java.util.ConcurrentModificationException

并发修改异常,ArrayList不是线程安全的。

解决方法:

Vector

是线程安全的,可以将ArrayList替换成Vector

List list = Collections.synchronizedList(new ArrayList());

List list = new CopyOnWriteArrayList();

CopyOnWriteArrayList源码:

其实现原理是,复制一个数组向其中添加元素,然后将复制的新数组赋值给

CopyOnWriteArrayList

,这样间接向

CopyOnWriteArrayList

中插入了数据,且使用Lock锁保证了线程安全

/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } } CopyOnWriteArraySet

同理,set也会有线程不安全的问题,可以使用CopyOnWriteArraySet解决,也可以使用Collections工具类来解决

package org.example.unsafe; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; /** * @Author: sshdg * @Date: 2020/9/5 20:38 */ public class SetTest { public static void main(String[] args) { // Set set = new HashSet(); Set set = new CopyOnWriteArraySet(); for (int i = 1; i { set.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(Thread.currentThread().getName()+"->"+set); }, Integer.toString(i)).start(); } } } ConcurrentHashMap

原理和上面两种不同

package org.example.unsafe; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; /** * @Author: sshdg * @Date: 2020/9/5 21:38 */ public class MapTest { public static void main(String[] args) { // Map map = new HashMap(); Map map = new ConcurrentHashMap(); for (int i = 1; i { map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5)); System.out.println(Thread.currentThread().getName()+"->"+map); }, Integer.toString(i)).start(); } } } Callable实现多线程 package org.example.callable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @Author: sshdg * @Date: 2020/9/5 21:51 */ public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { MyThread myThread = new MyThread(); FutureTask futureTask = new FutureTask(myThread); new Thread(futureTask).start(); // get()会阻塞,如果call()方法有耗时的操作,会一直在这等着,因此常放在最后一行,或使用ajax的方式,让其在后台慢慢加载 Integer integer = futureTask.get(); System.out.println(integer); } } class MyThread implements Callable { @Override public Integer call() throws Exception { System.out.println("call()"); return 1024; } }

另:call方法执行的结果是会被缓存的,也就是说:如下代码只会输出一行

call()

new Thread(futureTask,"A").start(); new Thread(futureTask,"B").start();


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3