基础Java多线程

创建线程的三种方式 

1:继承thread类,重写run方法

2:实现runnable接口

3:实现callable接口

第一种方式:继承thread类

package com.wangjunji.qifeng;
//继承thread类
public class TestCreateThread extends Thread {

    public TestCreateThread(String name) {
        super(name);
    }
    //覆盖run方法
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            //如果继承thread的类s可以使用this.getId 或 this.getName
            System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId()+":"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //创建对象
        TestCreateThread myThreadName = new TestCreateThread("myThreadName");
        TestCreateThread myThreadName1 = new TestCreateThread("myThreadName1");
        //调用run方法
        myThreadName.setName("我的子线程1");
        myThreadName1.setName("我的子线程2");
        myThreadName.start();
        myThreadName1.start();
        myThreadName.join();
        myThreadName1.join();
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId()+":"+i);
        }
    }
}

获取和修改线程的名称

获取线程ID和线程名称

1、在thread的子类中调用this.getid() 或this.getName() 

2、使用thread.currentThread().getId()和thread.currentThread.getName()

修改线程名称

1、调用线程对象的setName方法

2、使用线程子类的构造方法赋值 看如上方法

案例:使用继承thread类实现4个窗口卖100张票

package com.wangjunji.qifeng.second;

public class TiceWIn  extends Thread{
    private int count =100;

    @Override
    public void run() {
        int num = count;
        for (int i = 0; i < num; i++) {
            count = count -1;
            System.out.println("Thread name:"+this.getName()+":Thread id is :"+this.getId()+":"+count);
        }
    }
}



package com.wangjunji.qifeng.second;

import com.wangjunji.qifeng.Ticks;

public class Sellor {
    public static void main(String[] args) {
        TiceWIn t1 = new TiceWIn();
        TiceWIn t2 = new TiceWIn();
        TiceWIn t3 = new TiceWIn();
        TiceWIn t4 = new TiceWIn();
        t1.setName("一号窗口");
        t2.setName("二号窗口");
        t3.setName("三号窗口");
        t4.setName("四号窗口");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

四个人卖1000张票

package com.wangjunji.qifeng;

public class SellTickDoor  {
    public static void main(String[] args) throws InterruptedException {
        Ticks ticks = new Ticks();
        SellTicks t1 = new SellTicks(ticks);
        SellTicks t2 = new SellTicks(ticks);
        SellTicks t3 = new SellTicks(ticks);
        SellTicks t4 = new SellTicks(ticks);
        t1.setName("第一名");
        t2.setName("第二名");
        t3.setName("第三名");
        t4.setName("第四名");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t1.join();
        t2.join();
        t3.join();
        t4.join();

    }
}


package com.wangjunji.qifeng;

public class SellTicks extends Thread {
    private Ticks ticks;
    Object obj = new Object();
    public SellTicks(Ticks ticks) {
        this.ticks = ticks;
    }

    @Override
    public void run() {
        while (true) {
            if(ticks.getCount()>0) {
                synchronized (obj) {
                    ticks.sellcount();
                    System.out.println(this.getName() + "卖了一张" + "余票还有" + ticks.getCount() + "张");
                }
                }else{
                break;
            }
        }
    }
}


package com.wangjunji.qifeng;

public class Ticks  {
    private int count =1000;

    public int getCount() {
        return count;
    }

    public void sellcount(){
        count = count -1 ;

    }
}

package com.wangjunji.qifeng;

public class MyRunable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("名字"+Thread.currentThread().getName()+"Threadid"+Thread.currentThread().getId()+"i"+i);
        }
    }
}



package com.wangjunji.qifeng;

public class Test {
    public static void main(String[] args) {
       /* MyRunable myRunable = new MyRunable();
        new Thread(myRunable).start();*/

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("名字" + Thread.currentThread().getName() + "Threadid" + Thread.currentThread().getId() + "i" + i);
                }
            }
        };
        new Thread(runnable).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("名字"+Thread.currentThread().getName()+"Threadid"+Thread.currentThread().getId()+"i"+i);
        }
    }

}

你和你女朋友共用一个银行卡,你向卡中存钱,你女朋友从卡中取钱,使用程序模拟过程

package com.wangjunji.qifeng.card;

public class BankCard {
    private double money;

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}



package com.wangjunji.qifeng.card;

public class AddMony implements Runnable{
    BankCard bankCard;
    public AddMony(BankCard bankCard) {
        this.bankCard = bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            double money = bankCard.getMoney();
            money = money+1000;
            bankCard.setMoney(money);
            System.out.println(Thread.currentThread().getName()+"存入一千元"+bankCard.getMoney());
        }
    }
}


package com.wangjunji.qifeng.card;

public class SubMony implements Runnable{
    BankCard bankCard;
    public SubMony(BankCard bankCard) {
        this.bankCard = bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            double money = bankCard.getMoney();
            if(money<0){
                System.out.println("余额不足");
            }else{
                money = money-1000;
                bankCard.setMoney(money);
                System.out.println(Thread.currentThread().getName()+"取出一千元"+bankCard.getMoney());
            }

        }
    }
}
package com.wangjunji.qifeng.card;

public class TestBankCard {
    public static void main(String[] args) {
        BankCard bankCard = new BankCard();
        AddMony ad = new AddMony(bankCard);
        SubMony  sd = new SubMony(bankCard);
        Thread chen = new Thread(ad,"晨晨");
        Thread bing = new Thread(sd,"冰冰");
        chen.start();
        bing.start();
    }
}

线程的基本状态

线程对象被创建,即为初始状态  new 初始状态 调用start后进入ready就绪状态 os选中/时间片到期  running运行状态

调用start()之后,进入就绪状态,等待os选中,并分配时间片。

常见方法

休眠 public static void sleep 

设置线程优先级

优先级:

     线程对象.setPriority()

     线程优先级1-10,默认为5 ,优先级越高,表示获取cpu机会越多

守护线程:

     线程对象.setDaemon(true);设置守护线程

   线程有两类:用户线程(前强线程),守护线程(后台线程)

如果程序中所有前台线程都执行完毕,后台线程会自动结束

垃圾回收器线程属于守护线程 

package com.wangjunji.qifeng.card;

public class PriorityThread extends Thread {
    public void run(){
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }

    public static void main(String[] args) {
        PriorityThread priorityThread = new PriorityThread();
        PriorityThread priorityThread1 = new PriorityThread();
        priorityThread.setName("thread-1");
        priorityThread1.setName("thread-2");
        priorityThread.setPriority(10);
        priorityThread1.setPriority(1);
        priorityThread.start();
        priorityThread1.start();
    }
}

  线程安全问题:

A线程---查找下标0,时间片到期  --->将hello存入下标为0的位置

B线程---查找下标0,时间片到期--->将world存入下标的位置

需求:A线程将hello存入数组

         B线程将world存入数组

多线程安全问题:

     当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。

    临界资源:共享资源(同一对象),一次仅允许一个线络使用,才可能保证其正确性。

    原子操作,不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。

package com.wangjunji.qifeng;

public class ThreadSafe {
    private static int index=0;
    public static void main(String[] args) throws InterruptedException {

        String[] str = new String[5];
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                str[index] = "hello";
                index++;
            }
        };

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                str[index] = "world";
                index++;
            }
        };
        Thread thread = new Thread(runnable);
        Thread thread1 = new Thread(runnable1);
        Thread thread3= new Thread(runnable);
        Thread thread4 = new Thread(runnable1);
        thread.start();
        thread1.start();
        thread3.start();
        thread4.start();
        thread.join();
        thread1.join();
        thread3.join();
        thread4.join();

        for (String s : str) {
            System.out.println(s);
        }
    }
}

思考:在程序中应用中,如何保证线程的安全性。

同步方式(1):

     同步代码块:synchronized(临界资源对象) //对临界资源对象加锁

//代码(原子操作)

package com.wangjunji.qifeng;

public class ThreadSafe {
    private static int index=0;
    public static void main(String[] args) throws InterruptedException {
      
        String[] str = new String[5];
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                synchronized (str){
                str[index] = "hello";
                index++;}
            }
        };

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (str){
                    str[index] = "world";
                    index++;}
            }
        };
        Thread thread = new Thread(runnable);
        Thread thread1 = new Thread(runnable1);
        Thread thread3= new Thread(runnable);
        Thread thread4 = new Thread(runnable1);
        thread.start();
        thread1.start();
        thread3.start();
        thread4.start();
        thread.join();
        thread1.join();
        thread3.join();
        thread4.join();

        for (String s : str) {
            System.out.println(s);
        }
    }
}

每个对象都有一个互斥锁标记,用来分配给线程

只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。

基础Java多线程_第1张图片

   同步方法:

synchronized返回值类 方法名称(形参列表0) //对当前对象this加锁 ,如果是静态方法则是 class

同步方法:

注:只有拥有对象的互斥标记的线程,才能进入该对象加锁的同步方法中。

线程退出同步方法时,会释放相应的互斥锁标记。

 

同步规则:

注意:只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。

如调用不包含不包含代码的方法,或普通方法时,则不需要锁标记,可直接调用。

已知jdk中线程安全的类

stringbuffer

vector

hashtable

以上类中的公开方法,均为synchnoized的同步方法。

 

经典问题:

死锁:

当第一个线程拥有a对象锁标记,并等待b标记,同时第二个线程拥有b对象锁标记,并等待A对象锁标记时,产生死锁。

一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,因此可能造成死锁。

 

//死锁代码
package com.wangjunji.qifeng.card;

public class Resrouce  {
    public static Object objA = new Object();
    public static Object objB = new Object();
}

//
package com.wangjunji.qifeng.card;

public class BoyInfo extends Thread {
    public void run(){
        synchronized (Resrouce.objA){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("boygetInfo get a");
            synchronized (Resrouce.objB){
                System.out.println("boygetinfo get b ");

            }
        }
    }
}

//
package com.wangjunji.qifeng.card;

public class grirlInfo extends Thread{
    public void run(){
        synchronized (Resrouce.objB){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("grirlInfo get b");
            synchronized (Resrouce.objA){
                System.out.println("grirlInfo get a ");

            }
        }
    }
}

package com.wangjunji.qifeng.card;

public class UseInfo {
    public static void main(String[] args) {
        BoyInfo boyInfo = new BoyInfo();
        grirlInfo grilinfo = new grirlInfo();
        boyInfo.start();
        grilinfo.start();
    }
}

线程通信

等待:等待队列

public final void wait()

public final void wait(long timeout )

必须在对obj加锁的同步代码块中,在一个线程中,调用obj.wait()时,此线程会释放其拥有所有锁标记,同时此线程阻塞在o的等待队列中,释放锁,进入等待队列。

通知:

public final void notify()

public final void notifyall()

经典问题:

生产者,消费者

若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使用生产者和消费者能并发执行,在两者之间一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产向一个满的缓冲区中放入产品。

 

线程的创建:

继承thread 

实现runnable接品,传入给thread对象执行。

 

线程安全:

      同步方法块:为方法中的局部代码加锁

     同步方法,为方法的所有代码加锁

高级多线程:

 1)线程池概念

问题:

    线程是宝贵的内存资源,单个线程约占1Mb空间,过多分配易造成内存溢出。

   频繁的创建及销毁线程会增加虚拟机回收频率,资源开销,造成程序性能下降。

池程池:

   线程容器,可设定线程分配的数量的上限。

    将预先创建的线程对象存入池中,并重用线程池中的线程对象

    避免频繁的创建和销毁。

线程池原理:

  task1

   task2        -----> threadpool  

   task3

将任务提交给线程池,由线程池分配线程,运行任务,并在当前任务结束后复用线程。

创建线程池:

常用的线程池接口和类(所有包java.util.concurrent里)

Executor  线程池的顶级接口

executorservice:线程线接口,可通过submit(runable task)提交任务代码。

executors工厂类,通过此类可以获得一个线程池。

通过newFixedThreadpool(int nthreads)获取固定数量的线程池,参数指线程池中线程的数量。

通过newCachedthreadpools获得动态数量的线程池,如不够则创建新的,没有上限。

创建线程池

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 演示线程池的创建
 * Executor:线程池的根接口,execute()
 * executorService:包含管理线程池的一些方法,submit shutdown
 *  threadpoolExecutor
 *  scheduledthreadpoolexecutor
 * executor:创建线程池的工具类
 *   (1) 创建固定线程个数池程池
 *   (2) 创建缓存线程池,由任务的多少决定
 *     (3):创建单线程池
 *     (4)创建调度线路程池,调度,周期,定时执行。
 *
 */
public class Demo01 {
    public static void main(String[] args) {
        //设置提交的个数
        //ExecutorService executorService = Executors.newFixedThreadPool(4);
        //不设置提交的个数,创缓存线程池,线程个数
        ExecutorService executorService = Executors.newCachedThreadPool();
        // 创建单线程Executors.newSingleThreadExecutor();
        //创建调度线程池,调度,周期,定时执行 Executors.newScheduledThreadPool(5);
        Runnable runnable = new Runnable() {
            private int ticket = 10000;
            public void run() {
                while (true){
                    if(ticket<=0){
                        break;
                    }
                    ticket--;
                    System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
                }
            }
        };
        //提交任务
        for (int i = 0; i < 4; i++) {
            executorService.submit(runnable);
        }
        //关闭线程,不然程序结束不了,等待所有任务执行完毕,才能结束。
         executorService.shutdown();

    }
}

Callable接口:

public interface Callable {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception; 
}

jdk1.5加入,与runnable接口类似,实现之后代表一个线程任务

callable具有泛型返回值,可以声明异常。

package com.wangjunji.qifeng.threadpool;


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

/**
 * 演示callable接口的使用
 * callable和runable接口的区别
 * callable接口中call方法有返回值,runbable中run方法没有返回值
 * callable接口中call方法有声明异常,runable接口中run方法没有异常
 */
public class Demo02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //功能需求,使用callable实现1~100的和
        //创建callable对象
        Callable callable= new Callable() {
            @Override

            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName()+"开始计算");
                int sum=0;
                for (int i = 1; i <= 100; i++) {
                    sum=sum+i;
                }
                return sum;
            }
        };
        //2。不能交给线程。需要把callable对象转成任务
        FutureTask task = new FutureTask<>(callable);
        //创建线程
        Thread thread = new Thread(task);
        //启动线程
        thread.start();
        //获取 结果,等等call执行完毕,返回结果
        Integer integer = task.get();
        System.out.println(integer);
    }
}

使用callable与线程池结合起来使用

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.*;

/**
 * callable与线程池结合起来用
 * 使用线程池来计算1-100的和
 */
public class Demo03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        //提交任务,表示将要执行完任务的结果
        Future taskinfo = executorService.submit(new Callable() {
            @Override
            public Integer call() throws Exception {
                System.out.println("开始执行作务");
                int sum = 0;
                for (int i = 0; i <= 100; i++) {
                    sum = sum + i;
                }
                return sum;
            }
        });
        //3获取结果
        System.out.println(taskinfo.get());
        executorService.shutdown();

    }
}

Future接口 :

future表示将要完成任务的结果

需求,使用两个线程,并发计算1~50,51~100的和,再进行汇总计算。

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.*;

public class Demo04 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService task =   Executors.newFixedThreadPool(2);
        Future item1 = task.submit(new Callable() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 0; i < 50; i++) {
                    sum = sum + i;
                }
                return sum;
            }
        });
        Future item2 = task.submit(new Callable() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 50; i <= 100; i++) {
                    sum = sum + i;
                }
                return sum;
            }
        });
        int allcount =item1.get()+item2.get();
        System.out.println(allcount);
        task.shutdown();

    }
}

future接口

表示executorservice.sumbit所返回的状态结果,就是call的返回值

方法:v get()以阻塞形式等待,future中的异步处理结果(call的返回值)

思考什么是同步,什么是异步

同步:形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续

异步:形容一次调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回,二者竞争时间片,并发执行。

Lock接口:

jdk5加入,与synchronized比较,显示定义,结构更灵活。

提供更多实用方法,功能更强大,性能更优越。

常用方法:

void lock()://获取锁,如锁被占用,则等待

boolean tryLock()://尝试获取锁,成功返回true,失败返回false,不阻塞

void unlock释放锁。

重入锁:

拿多次锁,称为重入锁

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo06 implements Runnable {
    private int tickCount = 100;
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            lock.lock();
            try{
                if(tickCount<=0){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+":"+tickCount);
                tickCount--;
            }finally {
                lock.unlock();
            }
        }



    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        Demo06 demo06 = new Demo06();
        executorService.submit(demo06);
        executorService.submit(demo06);
        executorService.submit(demo06);
        executorService.submit(demo06);
        executorService.shutdown();
    }
}

读写锁:

reentantreadwritelock

一种支持一写多读的同步锁,读写分离,可分别分配读锁,写锁

支持多次分配读锁,使用多个读操作可以并发执行。

互斥规则,

写一写,互斥,阻塞

读-写,互斥,读阻塞,写阻塞读

读一读,不互斥,不阻塞

在读操作远远高于写操作的环境中,可保障线程安全情况下,提高运行效率。

package com.wangjunji.qifeng.threadpool;


import java.security.Security;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

public class Demo07 {
    private ReentrantReadWriteLock rr1 = new ReentrantReadWriteLock();

    private ReadLock readLock = rr1.readLock();
    private WriteLock writeLock = rr1.writeLock();
    private String value;

    public String getValue(){
        readLock.lock();
        try{
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("读取value"+value);
            return this.value;
        }  finally {
            readLock.unlock();
        }


    }

    public void setValue(String value){
        writeLock.lock();
        try{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("写入value"+value);
            this.value = value;
        }finally {
            writeLock.unlock();
        }
        this.value = value;
    }


    public static void main(String[] args) {
        Demo07 demo07 = new Demo07();
        //创建20个的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(40);
        for (int i = 0; i < 22; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    demo07.setValue(Thread.currentThread().getName()+new Random().nextInt(100));
                }
            });
        }

        for (int i = 0; i < 18; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    demo07.getValue();
                }
            });
        }

        executorService.shutdown();
        //如果是true,则执行完成
        executorService.isTerminated();
    }

}

线程安全的集合

collection体系集合中,除vector以外的线程安全集合。

package com.wangjunji.qifeng.threadpool;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class Dem09 {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList<>();
//使用collections中的线程安全方法转换线程安全的集合。
        Collection strings = Collections.synchronizedCollection(arrayList);
//java并发包里面的
 /*ArrayList arrayList = new ArrayList<>();
        Collection strings = Collections.synchronizedCollection(arrayList);*/
        CopyOnWriteArrayList strings = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            int temp = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i1 = 0; i1 < 10; i1++) {

                        strings.add(Thread.currentThread().getName()+":"+temp+":"+i1);
                        System.out.println(strings.toString());
                    }
                }
            }).start();
        }
    }
}

CopyOnWriteArrayList:

线程安全的arraylist,加强版的读写分离

写有锁,读无锁,读写之间不阻塞,优于读写锁。

写入时,先copy一个容器副本,再添加新元素,最后替换引用。 以空间来换取安全。

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo10 {
    public static void main(String[] args) {
        CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    for (int i1 = 0; i1 < 100; i1++) {
                        list.add(i1);
                    }
                }
            });
        }
  
        //这个结束的时候,有可能所有的子任务还没有完成,需要增加以下的代码
        executorService.shutdown();
        System.out.println(list.size());
        while (!executorService.isTerminated()){

        }
        System.out.println(list.size());
    }
}

CopyOnWriteArraySet

线程安全的Set,底层使用copyOnWriteArrayList实现

唯一不同在于,使用addifAbsent添加元素,会遍历数组

如果存在元素,则不添加(扔掉副本)

Quene接口(队列)

collection的子接口,表示队列Fifo()先进先出 

栈,先进后出

常用方法:

    都需要抛出异常

   boolean add(E e):顺序添加一个元素(到达上限后,再添加则会抛出异常)

  E remove 获得一个元素并移除(如果队列没有元素,则抛出异常)

  E element() 获得一个元素,但不移除,如果队列没有元素,则抛出异常。

返回特殊值:推荐使用

boolean offer(E e)//顺序添加一个元素(到达上限后,再添加则会返回false)

E poll获得一个元素并移除 (如果队列没有元素时,则返回null)

E peek() 获得一个元素但不移除 (如果队列没有元素时,则返回null)

package com.wangjunji.qifeng.threadpool;

import java.util.LinkedList;
import java.util.Queue;

public class Demo11 {
    public static void main(String[] args) {
        Queue fruits = new LinkedList<>();
        fruits.offer("苹果");
        fruits.offer("葡萄");
        fruits.offer("香蕉");
        fruits.offer("梨子");
        fruits.offer("葡萄");
        fruits.offer("苹果");
        fruits.offer("葡萄");
        fruits.offer("香蕉");
        fruits.offer("梨子");
        fruits.offer("葡萄");
        System.out.println(fruits.size());
        int length = fruits.size();
        for (int i = 0; i < length; i++) {

            System.out.println(fruits.poll());
        }
    }
}

ConcurrentLinkedQueue

线程安全、可高效读写的队列,高并发下性能最好的队列

无锁,cas 比较交换算法,修改的方法包含三个核心参数(Ven)

v要更新的变量

E预期值

N新值。

只有当v==e时,v=n,否则表示已被更新过,则取消当前操作。

BlockingQueue接口(阻塞队列)

Queue的子接口,阻塞队列,增加了两个线程状态为无限期的等待的方法。

方法

void put(E e)将指定的元素插入此队列中,如果没有可用空间,则等待

E take 获得并移除此队列的头部元素,如果没有可用元素,则等待。 

可用于解决产生、消费的问题

阻塞队列

ArrayBlockingQueue:

数组结构实现,有界队列(手工固定上限)

BlockingQueue abq = new ArrayBlockingQueue (10);

LinkedBlockingQueue 

链表结构实现,有界队列(默认上限integer.Max_Value)

BlockingQueue abq = new LinkedBlockingQueue  (10);

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;

/**
 * 阻塞队列的使用
 * 案例1:创建一个有界队列,添加数据
 * 案例2:使用阻塞队列实现生产者和消费者
 */
public class Demo13 {
    public static void main(String[] args) throws Exception {
        ArrayBlockingQueue queue  = new ArrayBlockingQueue<>(3);

            queue.put("aaa");
            queue.put("aaa");
            queue.put("aaa");
            queue.put("aaa");
            

    }
}

生产者,消费者

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ArrayBlockingQueue;

public class Demo14 {
    public static void main(String[] args) {
        ArrayBlockingQueue queue  = new ArrayBlockingQueue<>(6);
        Thread zhengzheng = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {

                    try {
                        System.out.println("生产了第" + i + "个面包");
                        queue.put(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread bingbing = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {

                    try {
                        System.out.println("消费" + queue.take() + "个面包" );

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        zhengzheng.start();
        bingbing.start();

    }
}

ConcurrentHashMap:

初始容量默认为16段,使用分段锁设计

不对整个map加锁,而是为每个segment加锁

当多个对象存入同一个segment时,才需要互斥

最理想的状态为16个对象分别存入16个segemnt,并行数量为16.

使用方式与hashmap无异

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo15 {
    public static void main(String[] args) {
        ConcurrentHashMap hashMap = new ConcurrentHashMap();
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    for (int i1 = 0; i1 < 1000; i1++) {
                        hashMap.put(Thread.currentThread().getName()+":"+i1,"value"+ String.valueOf(Math.random()*1000));
                    }
                }
            });

        }
        executorService.shutdown();
        while (!executorService.isTerminated()){

        }

        System.out.println(hashMap);
    }
}

基础Java多线程_第2张图片

 

你可能感兴趣的:(java,java面试)