Java多线程+JUC并发


Java多线程

 1.几种多线程的创建方式:

package com.swain.programmingpearls.thread;
 
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
 
/**
 * thread 的几种实现
 */
public class threadTest {

    /**
     * 继承thread类
     */
    public static class ExtendsThread extends Thread {
        public void run(){
            System.out.println("方法一 继承Thread线程:" + Thread.currentThread().getName());
        }
    }
 
    /**
     * 实现runnable
     */
    public static class AchieveRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("方法二 实现Runnable:" + Thread.currentThread().getName());
        }
    }
 
    /**
     * 通过Callable和FutureTask创建线程
     */
    public static class TestCallable implements Callable {
        @Override
        public String call() throws Exception {
            System.out.println("方法三 实现callable:" + Thread.currentThread().getName());
            return "我是callable的返回";
        }
    }
//测试===============
    public static void main (String[] args) {
        //继承thread
        ExtendsThread extendsThread = new ExtendsThread();
        extendsThread.start();
 
        //实现runnable
        Thread runThread = new Thread(new AchieveRunnable());
        runThread.start();

        //runable 匿名内部类方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("实现Runnable 匿名内部类方式:" + Thread.currentThread().getName());
            }
        }).start();
 
        //runnable Lamda表达式
        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("Lamda表达式:" + i);
            }
        }).start(); 

        //调用callable 可以有返回值 可以捕获异常
        Callable tc = new TestCallable();
        FutureTask task = new FutureTask(tc);
        new Thread(task).start();
        try {
            System.out.println(task.get());//获取返回值
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
 
 
        //ExecutorService 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);
       // 创建多个有返回值的任务  
     List list = new ArrayList();  
  
for (int i = 0; i < taskSize; i++) {     Callable c = new MyCallable(i + " ");     // 执行任务并获取Future对象      Future f = pool.submit(c);    // System.out.println(">>>" + f.get().toString());    list.add(f);    }
  
// 关闭线程池    pool.shutdown();   // 获取所有并发任务的运行结果    for (Future f : list) {    // 从Future对象上获取任务的返回值,并输出到控制台    System.out.println(">>>" + f.get().toString());    } }

2.线程/对象的几种方法

  线程方法:

  1. start()方法,开始执行该线程;
  2. stop()方法,强制结束该线程执行;不建议使用
  3. join方法,当前运行线程调用另一个线程的join方法,当前线程进入阻塞状态直到另一个线程运行结束等待该线程终止。
  4. sleep()方法,会让当前线程休眠进入阻塞状态并释放CPU,提供其他线程运行的机会且不考虑优先级,但如果有同步锁则sleep不会释放锁即其他线程无法获得同步锁  可通过调用interrupt()方法来唤醒休眠线程
  5. run()方法,直接执行线程的run()方法,但是线程调用start()方法时也会运行run()方法,区别就是一个是由线程调度运行run()方法,一个是直接调用了线程中的run()方法!!
  6. yield()方法,让出CPU调度,类似sleep只是不能由用户指定暂停多长时间 ,并且yield()方法只能让同优先级的线程有执行的机会。 yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。调用yield方法只是一个建议,告诉线程调度器我的工作已经做的差不多了,可以让别的相同优先级的线程使用CPU了,没有任何机制保证采纳。
  7. setpriority(),设置优先级:1-10,数字越大执行的概率越大

  对象方法:

  1. wait()方法使当前线程暂停执行并释放对象锁标志,让其他线程可以进入Synchronized数据块,当前线程被放入对象等待池中。 
  2. notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中的线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。 
  3. notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。 
  4. 它们都是用于协调多个线程对共享数据的存取,所以必须在Synchronized语句块内使用这三个方法。前面说过Synchronized这个关键字用于保护共享数据,阻止其他线程对共享数据的存取。但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出Synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制

 sleep与Wait的区别:

  1.  sleep是线程方法,wait是object方法
  2.  sleep ,释放cpu资源,不释放锁资源,如果线程进入sleep的话,释放cpu资源,如果外层包有Synchronize,那么此锁并没有释放掉。
  3.  wait,释放cpu资源,也释放锁资源,一般用于锁机制中 肯定是要释放掉锁的,因为notify并不会立即调起此线程,因此cpu是不会为其分配时间片的,也就是说wait 线程进入等待池,cpu不分时间片给它,锁释放掉

3.线程的状态:

  3.1状态图:

   3.2 等待队列图解:

 4.线程的同步:

  1. synchronized(同步)
    /**
     * 修饰普通方法和静态方法:synchronized是一种互斥锁,锁的是对象,synchronized修饰普通方法时,锁对象是this对象。修饰静态方法时,锁对象是.classs对象。
    所以多线程访问一个对象的同步方法时候拿到的都是同一把锁=》数据会同步,但当访问多个对象的同步方法时候,普通方法获取的不是同一把锁,静态方法是同一把锁
    对于非同步方法的访问还是并行的不安全
*/ private synchronized void method() { } private static synchronized void method2() { }
     /**
         * 修饰代码块,修饰class 和锁对象的区别类似于修饰静态方法和普通方法
         */    
  // 自定义锁对象
    private static Object object = new Object();
    @Override
    public void run() {
        /**
         * 修饰class 
         */
        synchronized(Condition2.class) {
        }
        /**
         * 锁对象
         */
        synchronized(this) {
        }
        synchronized(object) {
        }

    }

  2. ReentrantLock (可重入锁)

Lock lock = new ReentrantLock();
boolean b = lock.tryLock();
try {
Condition condition = lock.newCondition();
condition.await();
condition.signalAll();
condition.signal();//可以多new几个condition,实现顺序唤醒执行
    System.out.println("===");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}

synchronized和Lock区别

1.synchronized是一个关键字而lock是一个接口(lock、lockInterruptibly、tryLock、unlock、newCondition)。
2.synchronized是隐式的加锁,lock是显示的加锁。
3.synchronized可以作用在方法和代码块上,而lock只能作用在代码块上。
synchronized作用在静态方法上锁的是当前类的class,作用在普通方法上锁的是当前类的对象。
在javap反编译成字节码后,synchronized关键字需要有一个代码块进入的点monitorenter,代码块退出和代码块异常的出口点monitorexit。
4.synchronized是阻塞式加锁,而lock中的trylock支持非阻塞式加锁。
5.synchronized没有超时机制,而lock中的trylcok可以支持超时机制。
6.synchronized不可中断,而lock中的lockInterruptibly可中断的获取锁。(ReentrantLock.lockInterruptibly允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。 ReentrantLock.lock方法不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态,然后再中断线程。)
7.synchronized采用的是monitor对象监视器,lock的底层原理是AQS
8.synchronized只有一个同步队列和一个等待队列,而lock有一个同步队列,可以有多个等待队列。
同步队列:排队取锁的线程所在的队列。
等待队列:调用 wait 方法后,线程会从同步队列转移到等待队列。
9.synchronized是非公平锁,而lock可以是公平锁也可以是非公平锁。
10.synchronized用object的notify方法进行唤醒,而lock用condition进行唤醒。
11.lock有ReadWriteLock支持并发读。
=====

CountDownLatch

CycliBarrier

SemaPhore

 ==

ReentrantReadWriteLock

阻塞队列 BlockingQueue

 

 

AbortPolicy如果线程数>max+阻塞队列大小 会抛出异常!

超出时交给原来的线程执行

 ForkJion 

并行流

  1. Atomic( 原子类,效率较高,可用于优化 )
  2. ThreadLocal (线程本地变量)

JUC并发