线程安全问题:
如果多个线程同时执行一个任务,意味着他们共享同一种资源,由于线程CPU的资源不一定可以被谁抢占到,这时,第一条线程先抢占到CPU资源,他刚刚进行了第一次操作,而此时第二条线程抢占到了CPU的资源,共享资源还来不及发生变化,就同时有两条数据使用了同一条资源。
解决线程安全问题思路:
让一条线程占用共享资源时,阻止第二条线程同时抢占CPU的执行权,在代码中,我们只需要在方法中使用同步代码块即可。
ArrayList并发修改异常
ArrayList的线程不安全问题:
public class ThreadDemo {
public static void main(String[] args) {
//创建ArrayList集合
ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
new Thread(()->{
//向集合中添加那内容
list.add(UUID.randomUUID().toString().substring(0,8));
//向集合获取内容
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
ArrayList线程不安全解决方案
解决方案1:Vector
//Vector解决 List<String> list=new Vector<>();
解决方案2:synchronizedList
//Collections解决 List<String> list= Collections.synchronizedList(new ArrayList<>());
解决方案3: CopyOnWriteArrayList 写时复制技术
//CopyOnWriteArrayList解决 java.util.concurrent.CopyOnWriteArrayList List<String> list= new CopyOnWriteArrayList<>();
写时复制技术,并发读,独立写
wait和sleep
wait和sleep都可以使得线程阻塞,阻塞的三种情况:
(01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
(02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。
sleep()和wait(),使线程阻塞,写在同步代码块中,多线程--->单线程
1、在同步代码块中,sleep()不会释放锁,别的线程就得等着。而wait()方法会让当前线程阻塞的同时,释放锁。
2、sleep是Thread类的方法,wait是Object类中定义的方法。
3、调用wait后,需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间。
4、sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
5、wait可以指定时间,也可以不指定;⽽sleep必须指定时间
6、wait必须放在同步块或同步⽅法中,⽽sleep可以再任意位置
try{
//当前线程睡眠1秒,进入阻塞。苏醒后回到就绪状态
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
java.util.concurrent.TimeUnit
try{
//当前线程睡眠1秒,进入阻塞。苏醒后回到就绪状态
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e){
e.printStackTrace();
}
wait、notify、notifyAll
1、wait()方法,执行此方法,执行此方法的当前线程就进入阻塞状态,并释放锁
2、notify():执行此方法,就随机唤醒被waitt的一个线程。
3、notifyAll():执行此方法,会唤醒所有被wait的线程
使用wait(),notify()的一些注意点。
必须写在同步代码块中,调用者必须是同步监视器,是定义在object类中的
1.wait().notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中
2.wait().notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法的同步监视器
3.wait().notify(),notifyAll()三个方法是定义在java.lang.Object类中