一.多线程访问临界资源
1.多个线程访问一个资源,一个线程使用后来被抢走,临界资源问题就产生了。
2.解决方法:使用的时候上把锁,取钱的时候不能存,存的时候不要取钱即可。
3.锁 :任意的对象都可以被当做锁使用。
4.同步代码块 同步Synchronized有等待
(1)同步非静态方法 synchronize boolean 返回值boolean
(2)同步静态方法:在声明属性的时候加上static,变成非静态,方法返回值加上static。
异步Asynchronized 没有等待,各执行各的。
5.可重入锁(ReentrantLock)
在类中定义一个ReentrantLock,ReentrantLock lock=new ReentrantLock(); 上锁跟解锁是都必须有的,一个出现另一个也出现。用try catch的时候解锁要放在Fianlly中。
二.死锁
每个人都有其他人需要的资源,又要等待对方的资源,在获得所需要的资源之前又没有放弃之前拥有的资源。
***死锁的条件:1.两个以上的线程 2.至少两个锁以上 3.同步中嵌套同步。
三.多线程在单例中的应用:饿汉式(无线程问题)和懒汉式(有线程问题)
***单例实现的步骤:1.私有化构造方法2.在类中创建对象 3.通过公开的方法返回这个对象
private SingleTon(){}
private static SingleTon instance =new SingleTon();
public static SingleTon getInstance(){
return instance;
}
懒汉式写法需要解决线程问题,1.禁止反射破解2.加volatile3.加锁解锁
public class SingleTon {
private SingleTon(){
//禁止反射破解
synchronized (SingleTon.class) {
if (instance != null) {
throw new RuntimeException("不能使用反射创建对象");
}
}
}
private static volatile SingleTon instance; //volatile:不稳定的,易挥发的
public static SingleTon getInstance() {
if(singleTon==null) {//为了提高效率
synchronized (SingleTon.class) {//判断锁的过程比较耗性能,为了提高效率
if (singleTon == null) {
singleTon = new SingleTon();
}
}
}
return singleTon;
}
}
/**
* 线程类
* @author wgy
*
*/
public class SingleTonThread extends Thread{
@Override
public void run() {
SingleTon singleTon=SingleTon.getInstance();
System.out.println(singleTon.hashCode());
}
}
package com.qf.day20_6;
public class Test {
public static void main(String[] args) {
//线程对象
SingleTonThread s1=new SingleTonThread();
SingleTonThread s2=new SingleTonThread();
SingleTonThread s3=new SingleTonThread();
//启动线程
s1.start();
s2.start();
s3.start();
}
}
静态内部类写法
public class SingleTon2 {
private SingleTon2(){
}
static class Holder{
private static final SingleTon2 INSTACNE=new SingleTon2();
}
public static SingleTon2 getInstance(){
return Holder.INSTACNE;
}
}
使用枚举
public enum SingleTon3 {
INSTANCE;
public static SingleTon3 getInstance(){
return INSTANCE;
}
}
四.线程的通信:生产者与消费者设计模式
描述的是一块有缓冲区作为仓库,生产者将产品放到仓库,消费者从仓库拿走商品。
wait()等待 缓冲区已经满了或空的时候,生产者消费者停止自己的执行放弃锁,让自己处于等待状态,让其他线程运行。
notify()唤醒 :生产者或者消费者放入信号,让其他等待的线程执行,同时放弃锁,让自己处于等待状态。
notifyall()全部唤醒 唤醒所有等待的对象。
使用锁写生产和消费面包
public class BreadCon {
private Bread con;
private boolean flag;
private Lock lock=new ReentrantLock();
Condition proCondition=lock.newCondition();
Condition conCondition=lock.newCondition();
}
public void input(Bread b) {
lock.lock();
try {
while (flag) {
try {
proCondition.await();
} catch (Exception e) {
// TODO: handle exception
}
}
con = b;
System.out.println(Thread.currentThread().getName() + "生产了" + b.getId() + "面包");
flag = true;
conCondition.signal();
} finally {
lock.unlock();
}
}
public void output() {
lock.lock();
try {
while (!flag) {
try {
conCondition.await();
} catch (Exception e) {
// TODO: handle exception
}
}
Bread b = con;
System.out.println(Thread.currentThread().getName() + "消费了" + b.getId() + "面包, 生产者名字:" + b.getProductName());
con = null;
flag = false;
proCondition.signal();
} finally {
lock.unlock();
}
}
}
读写锁: 可以实现多个线程同时读取数据,写线程需要互斥执行 读写 写写互斥 读读不需要互斥
线程池:维护成一个队列,然后让其他的线程处于等待状态的线程,不用每次都创建新的线程。
ExecutorService threadPool = Executors.newFixedThreadPool(4);
ExecutorService threadPool = Executors.newFixedThreadPool(4);
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
实现类:
1 ThreadPoolExecutor:ExecutorService的实现类,负责线程池的创建使用。
2 ScheduledThreadPoolExecutor:继承 ThreadPoolExecutor,并实现 ScheduledExecutorService接口,既有线程池的功能,又具有线程调度功能。
3 Executors:线程池的工具类,负责线程池的创建。
newFixedThreadPool();创建固定大小的线程池。
newCachedThreadPool();创建缓存线程池,线程池大小没有限制。根据需求自动调整线程数量。
newSingleThreadExecutor();创建单个线程的线程池,只有一个线程。
newScheduledThreadPool();创建固定大小的线程池,可以延迟或定时执行任务。