J.U.C学习笔记

198 阅读3分钟
  • 1.volatile

    当多个线程进行操作共享数据时,可以保证内存中的数据可见 相较于synchronized 是一种较为轻量的同步策略 注意

    volatile 不具备互斥性 volatile 不能保证原子性 作用

    并发中保证有序性和可见性 应用场景:

    1.禁止程序指令重排(单例模式的双重锁) 2.保证内存的可见性(操作共享数据时线程之间内存可见)

  • 2.CAS算法

    保证数据的原子性(CAS是硬件对于并发操作共享数据的支持)是一种无锁的非阻塞算法的实现

    CAS包含三个操作(Compare And Swap)

        内存值V
        预估值A
        更新值B
        当且仅当V==A时,V = B,否则不做任何操作
    

    原子变量(java.util.cuncurrent)包下的类,采用的CAS算法

    AtomicInteger...

    class AtomicDemo implements Runnable{
    
      //	private volatile int i =0;
      	//原子变量
      	private AtomicInteger i= new AtomicInteger(0);
    
      	@Override
      	public void run() {
      		try {
      			Thread.sleep(200);
      		} catch (InterruptedException e) {
      			// TODO Auto-generated catch block
      			e.printStackTrace();
      		}
      		System.out.println(getI());
      	}
        public int getI() {
            return i.getAndIncrement();
        }
    
      }
    
  • 3.ConcurrentHashMap

    JDK1.7:采用的锁分段的机制(Segmen)粒度锁,每一个Segment都是HashMap,两次Hash算法 JDK1.8:采用的CAS,无锁机制 用法与HashMap相同 HashTable:锁的是整个表,同步变成异步操作,效率非常低 ConcurrentHashMap ConcurrentSkipListMap ConcurrentSkipListSet CopyOnWriteArrayList :ArrayList 注意: 添加操作多时,效率低,因为每次添加时都会进行复制,开销非常的大。并发迭代操作多时可以选择。 CopyOnWriteArraySet

  • 4.CountDownLatch:

    (闭锁)在完成某些运算时,只有其他所有线程完成运算时,当前运算才会进行
    

使用场景: 在主线程调用其他子线程时,让子线程全部执行完成之后再继续执行主线程。

用法:

	CountDownLatch latch = new  CountDownLatch(5);
	// 调用子线程处理
	子线程中调用
    try (子线程具体操作内容) 
    finally {
   	 	latch.countDown
     }//latch-1

	latch.await();
//继续处理主线程

CountDownLatch与join的区别:

调用thread.join() 方法必须等thread 执行完毕,当前线程才能继续往下执行,
而CountDownLatch通过计数器提供了更灵活的控制,只要检测到计数器为0当前线程就可以往下执行
而不用管相应的thread是否执行完毕。
  • 5.Callable:

    有返回值,可以抛出异常。 用法:

    FutureTask task =new FutureTask<>(new Callable);

    new Thread(task).start(); //接收返回值,类似于闭锁操作,必须等线程执行完之后菜会往下执行。 task.get();

  • 6.Lock:

    用于解决多线程安全问题的方式 用法:

    Lock lock=new ReentrantLock(); synchronized:隐式锁

    同步代码块 同步方法

    Lock:显示锁,需要通过lock()方法上锁,必须通过unlock()方法来释放锁

  • 7.wait()

      方法应该放在while中,而不是if中。
    
  • 8.Condition

用法:

 Lock lock = new ReentrantLock();  //创建lock对象
 Condition condition = lock.newCondition();  //获得Lock实例的Condition实例
lock的线程通信
await()
signal()
signalAll()

循環打印ABCABC...

public class TestABCAlternate {

  public static void main(String[] args) {
      Alternate alternate = new Alternate();

          new Thread(()->{
              for (int loop = 1;loop <=10; loop++) {
                  alternate.loopA(loop);
              }
          }, "A").start();

          new Thread(()->{
              for (int loop = 1;loop <=10; loop++) {
                  alternate.loopA(loop);
              }
          }, "B").start();

          new Thread(()->{
              for (int loop = 1;loop <=10; loop++) {
                  alternate.loopA(loop);
              }
          }, "C").start();
      }
}

class Alternate {

    private int number = 1;

    private Lock lock = new ReentrantLock();

    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void loopA(int loop) {
        lock.lock();

        try {
            if (number != 1) {
                try {
                    condition1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
//			for (int i = 1; i <= 5; i++) {
//				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + loop);
//			}
            System.out.print(Thread.currentThread().getName());
            number = 2;
            condition2.signal();

        } finally {
            lock.unlock();
        }
    }

    public void loopB(int loop) {
        lock.lock();

        try {
            if (number != 2) {
                try {
                    condition2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
//			for (int i = 1; i <= 10; i++) {
//				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + loop);
//			}
            System.out.print(Thread.currentThread().getName());
            number = 3;
            condition3.signal();

        } finally {
            lock.unlock();
        }
    }

    public void loopC(int loop) {
        lock.lock();

        try {
            if (number != 3) {
                try {
                    condition3.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
//			for (int i = 1; i <= 15; i++) {
//				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + loop);
//			}
            System.out.print(Thread.currentThread().getName());
            number = 1;
            condition1.signal();
        } finally {
            lock.unlock();
        }
    }
}
  • 9.ReadWriteLock:

    读写锁
    写写/读写 需要互斥
    读读不需要互斥
    

用法:

ReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();

lock.readLock().unlock();

lock.writeLock().lock();

lock.writeLock().unlock();
  • 10.线程8锁

    两个普通同步方法,两个线程,标准打印, 打印结果?   锁的对象是this(同一把锁)
    新增 Thread.sleep() 给 getOne(),打印结果?         锁的对象是this(同一把锁)
    新增普通方法 getThree() , 打印结果?				   锁的对象是this(同一把锁) getThree无锁
    两个普通同步方法,两个 Number 对象,打印结果?      锁的对象是this(不是同一把锁)
    修改 getOne() 为静态同步方法,打印结果?
    修改两个方法均为静态同步方法,一个 Number 对象,打印结果?
    一个静态同步方法,一个非静态同步方法,两个 Number 对象,打印结果?
    两个静态同步方法,两个 Number 对象,打印结果?      锁的对象是类对象(是同一把锁)
    

    ** 所有的非静态同步方法用的都是同一把锁:实例对象本身。**

    ** 所有的静态同步方法用的也是同一把锁:类对象本身。**

  • 11.线程池:

    提供了一个线程队列,队列中保存着所有等待的线程,
    避免了创建于销毁额外开销,提高了响应的速度。
    

体系结构

 Executor:负责线程的使用与调度的根接口
 ----ExecutorService 子接口
     -----ThreadPoolExecutor 实现类
  -----ScheduledExecutorService 子接口:负责线程的调度的子接口
       ----- ScheduledExecutorExecutor :实现类

Executors 工具类

    newFixedThreadPool: 创建固定大小的线程池  (LinkedBlockingQueue)
    newCachedThreadPool  缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量(SynchronousQueue)
    newSingleThreadExecutor 创建单个线程池。线程池中只有一个线程(LinkedBlockingQueue)
    newScheduledThreadPool   创建固定大小的线程池,可以延迟或定时执行任务 (DelayQueue)

用法:

ScheduledExecutorService service2 = Executors.newScheduledThreadPool(5);
service2.schedule(new ExecutorPool(), 1, TimeUnit.SECONDS);//延迟1秒执行任务
service2.scheduleAtFixedRate(new ExecutorPool(), 1, 3, TimeUnit.SECONDS);//延迟2秒,每隔1秒执行任务(循环)

执行线程的两种方式(execute和submit)

1.execute提交的是Runnable类型的任务,而submit提交的是Callable或者Runnable类型的任务
2.execute的提交没有返回值,而submit的提交会返回一个Future类型的对象
3.execute提交的时候,如果有异常,就会直接抛出异常,而submit在遇到异常的时候,
通常不会立马抛出异常,而是会将异常暂时存储起来,等待你调用Future.get()方法的时候,才会抛出异常

submit可以查看执行是否成功

ThreadPoolExecutor 参数:

int corePoolSize :该线程池中核心线程数最大值(核心线程默认情况下会一直存活在线程池中)
int maximumPoolSize:线程总数最大值
long keepAliveTime:非核心线程闲置超时时长
TimeUnit unit:闲置时长的单位
BlockingQueue<Runnable> workQueue:该线程池中的任务队列:维护着等待执行的Runnable对象
ThreadFactory threadFactory
RejectedExecutionHandler handler:抛出异常

workQueue类型:

SynchronousQueue:队列接接收任务,直接提交给线程。
LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,
                     则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。
                     队列没有限制
ArrayBlockingQueue:队列满了就则新建线程(非核心线程)执行任务。
DelayQueue:这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

RejectedExecutionHandler handler:

AbortPolicy:直接抛出异常,这是默认策略;
CallerRunsPolicy:用调用者所在的线程来执行任务;
DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
DiscardPolicy:直接丢弃任务