理论基础:线程间通信:
1、生产者+消费者
2、通知等待唤醒机制 wait 和notify
为什么一个关于线程的操作,方法却放在Object包下?
因为多线程的线程安全,必定依赖于锁,而任何对象都可以当锁对象,所以将公共的方法放入到Object类中。
多线程编程模板B: 判断 干活 通知
基于以上理论,我们写出第一版的代码(synchronized实现):
class ShareDataOne//资源类
{
private int number = 0;//初始值为零的一个变量
public synchronized void increment() throws InterruptedException
{
//1判断
if(number !=0 ) {
this.wait();
}
//2干活
++number;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//3通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException
{
// 1判断
if (number == 0) {
this.wait();
}
// 2干活
--number;
System.out.println(Thread.currentThread().getName() + "\t" + number);
// 3通知
this.notifyAll();
}
}
public class NotifyWaitDemoOne
{
public static void main(String[] args)
{
ShareDataOne sd = new ShareDataOne();
new Thread(() -> {
for (int i = 1; i < 10; i++) {
try {
sd.increment();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i < 10; i++) {
try {
sd.decrement();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, "B").start();
}
}
结果异常的完美,但是如果启动的是四个线程呢?
导致出现线程混乱的原因是:虚假唤醒
出现虚假唤醒的原因:
If只判断一次,while可以判断多次,wait()被唤醒后直接在原地执行,所以会出现以上这种情况。
解决方案: if ==> while
中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while
将if判断更改为while判断即可解决虚假唤醒
解决原理:
因为if只进行一次判断,假设当+1抢到了资源, 此时的num=1,其他三个线程进行资源抢夺,即使此时+1*抢到了资源,但是判断后,无法进行下面的代码,就沉睡下去,再当+1抢到资源时,就不会再从开始进行if判断了,从而会直接执行下面的++代码,使得num的值变为了2,导致线程不安全的情况,造成了虚假唤醒
添加一点关于线程安全的内容:
我们常见的ArrayList,HashSet, HashMap 的默认长度是多少,每次扩容多少?
ArrayList:
-
默认长度:10
-
ArrayList 默认长度是10 ,每次扩容50% ,底层使用的是Object[] 。
HashSet:
-
默认长度:16
-
扩容方式:HashSet底层是基于HashMap实现的,因此其扩容机制与HashMap类似。当HashSet中的元素数量达到容量与加载因子(默认为0.75)的乘积时,HashSet会进行扩容。扩容时,容量也会增加为原来的1.5倍。
HashMap:
-
默认长度:16
-
扩容方式:当HashMap中的元素数量达到容量与加载因子(默认为0.75)的乘积时,HashMap会进行扩容。扩容时,容量同样会增加为原来的1.5倍,并且所有键值对会重新哈希到新的位置上。
常见的集合ArrayList,HashSet, HashMap 他们是线程安全的吗?如果不安全,如何解决
都是线程不安全的,可以使用
-
使用Vectory
-
使用List<String> list = Collections.synchronizedList(new ArrayList<>());
Collections提供了方法synchronizedList保证list是同步线程安全的。
Collections.synchronizedList()、Collections.synchronizedMap()、Collections.synchronizedSet()同步集合类完善线程安全。
使用的是Collections工具类提供的内容。可以解决线程安全问题,但是效率不是特别高。
-
最终方案:
做法: list --> List<String> list = new CopyOnWriteArrayList();
Set -- > Set<String> set = new CopyOnWriteArraySet<>();
Map --> Map<String,String> map = new ConcurrentHashMap();
原理:使用了写时复制技术,速度要比同步锁快,类似于表锁和行锁的区别
CopyOnWriteArrayList是arraylist的一种线程安全变体,
多线程创建的四种方式:
-
实现runable接口,这是创建线程最传统的方式之一
-
继承thread类: 这是另一种创建线程的方式,通过继承
Thread
类并重写run()
方法。 -
使用线程池
-
使用Future和Callable
class MyThread implements Runnable{
@Override
public void run() {
}
}
新类MyThread2实现callable接口
class MyThread2 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
return 200;
}
}·
标签:HashMap,虚假,number,Collections,线程,new,唤醒
From: https://blog.csdn.net/qq_62984376/article/details/141604989