JAVA多线程高级 *** 作

JAVA多线程高级 *** 作,第1张

JAVA多线程高级 *** 作

目录

一,线程的状态

二,线程池

1,以前写多线程的弊端

2,线程池的实现步骤

3,代码实现

a:创建默认个数的线程池

b:创建指定个数的线程池

c:自己创建线程池

三,Volatile

四,原子性

1,AtomicInteger原子型类:

 2,使用原子性解决送冰淇凌问题

 3,AtomicInteger原理

4,Synchroized和CSA的区别

五,并发工具类

1,Hashtable

2,ConcurrentHashMap

3,CountDownLatch类

4,Semaphore类


一,线程的状态

线程的状态分为,新建状态,就绪状态,运行状态,死亡状态

当线程通过start()方法到达就绪状态的时候,强盗CPU的执行权就会到运行状态,没有就等待。

在运行状态会遇到阻塞队列,wait()等待方法,和sleep()都会使这条线程回到就绪状态。

 虚拟机中线程的六种状态:

新建状态(new)------ 创建线程对象

就绪状态(RUNNABLE) ------ start方法

阻塞状态(BLOCKED) ------ 无法获得到锁对象

等待状态(WAITING) ------ wait方法

计时状态(TIMED_WAITING) ------ sleep方法

结束状态(TERMINATED) ------ 全部代码运行完毕

二,线程池 1,以前写多线程的弊端

        (1),用到线程就创建

        (2),用完之后线程就消失

2,线程池的实现步骤

        (1),创建一个池子,池子中是空的

        (2),有任务需要执行时,才会创建线程对象,当任务执行完毕,线程对象归还给池子

3,代码实现

        (1),创建一个池子,池子中是空的 ------ 创建Executor中的静态方法

        (2),有任务需要执行时,创建线程对象  --------   submit方法   

                  任务执行完毕,线程对象归还给池子     

        (3),所有的任务全部执行完毕,关闭连接池  ------ shutdown方法

a:创建默认个数的线程池
public class MyThreadPoolDemo01 {
    public static void main(String[] args) throws InterruptedException {
        //1,创建一个默认的线程池对象,池子中默认是空的,默认最多可以容纳int类型的最大值。
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Executors -- 可以帮助我们创建线程池对象
        //ExecutorService --- 可以帮助我们控制线程池
        //Executors方法是静态的,通过类名调用,返回值是ExecutorService;

        executorService.submit(()->{
            Thread.currentThread().setName("线程一");
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
        Thread.sleep(2000);//睡眠的话,第一个线程结束。放进了线程池。
                                //下次开启线程的话,是从线程池中获取线程的。
        executorService.submit(()->{
        });

        executorService.shutdown();
    }
}

默认的线程池,最多可以 int类型最大值数量个线程

 ExectursService中的submit()方法会自动创建线程,线程执行完之后,也会自动返回到池子中

线程池执行完毕之后一定要使用shoutdown方法关闭。

b:创建指定个数的线程池
//        static ExecutorService new FixedThreadPool(int nThreads);
//        创建一个指定最多线程数量的线程池
public class MyThreadPoolDemo02 {
    public static void main(String[] args) throws InterruptedException {
        //这个里面的参数不是初始值,而是最大值。创建出来的线程池也是空的。
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        //强转
        ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;
        System.out.println(pool.getPoolSize());//这里时使用ThreadpoolExecutor中的
        //getPoolSize()方法获取线程池中的线程个数
        //线程一
        executorService.submit(() -> {
            Thread.currentThread().setName("线程一");
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });
        //线程二
        executorService.submit(() -> {
            Thread.currentThread().setName("线程二");
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });
        //线程三
        executorService.submit(() -> {
            Thread.currentThread().setName("线程三");
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });
        //关闭线程
        executorService.shutdown();
        //获取线程池中线程的个数
        System.out.println(pool.getPoolSize());

    }
}

 指定线程池中的最大数量就是表示线程池中最大可以有的线程数。

c:自己创建线程池

构造方法:

ThreadPoolExecutor  tpe   =   new  ThreadPoolExecutor

(核心线程数量,最大线程数量,空闲线程最大存活时间,存活时间单位,任务队列,创建线程工厂,任务的拒绝策略);

1,参数一:核心线程数量
2,参数二:最大线程数量
3,参数三:空闲线程最大存活时间
4,参数四:空闲线程存活时间的单位-- TimeUnit类
5,参数五:任务队列--阻塞队列ArrayBlockingQueue
         让任务在队列中等着,等有空闲线程了,再从这个队列中获取任务并执行。
6,参数六:创建线程工厂--怎么创建线程的对象--Executors中的静态方法,defaultThreadFactory()
           按照默认的方式创建线程对象
7,参数七:任务的拒绝策略,ThreadPoolExecutor中的内部类对象---new  ThreadPoolExecutor.AborPolicy();
         (1)什么时候拒绝
              当提交的任务大于池子中最大线程数量+任务队列容量
         (2)怎么拒绝

//自己创建线程池
public class ThreadPoolExecutorDemo01 {
    public static void main(String[] args) {
        MyThread01 mt = new MyThread01();
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(2,
                5, 2, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());


        tpe.submit(()->{//创建第一个线程
           Thread.currentThread().setName("线程一");
            System.out.println(Thread.currentThread().getName()+"执行了");
        });

        tpe.submit(()->{//创建第二个线程
            Thread.currentThread().setName("线程二");
            System.out.println(Thread.currentThread().getName()+"执行了");
        });

        System.out.println(tpe.getPoolSize());//获取线程的数量
        tpe.shutdown();//关闭线程池
    }
    //也可以使用创建资源类的方法来写,只需要把资源类对象传入tpe.submit()方法中。
}

 参数四是使用TimeUnit类来调用时间单位的

                Days:天

                HOURS:小时

                MICROSECONDS:微妙

                MILLISECONDS:毫秒

                MINUTES:分钟

                NANOSECONDS:纳秒

                SECONDS:秒

参数七,任务拒绝策略

        ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。默认的策略

        ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常,这是不推荐的做法

        ThreadPoolExecutor.DiscardOldestPolicy:抛弃队列中等待最久的任务然后把当前任务加入队列中

        ThreadPoolExecutor.CallerRunsPlolicy:调用run()方法绕过线程池直接执行

三,Volatile

        1,堆内存是唯一的,每一个线程都有直接的线程栈

        2,每一个线程在使用堆里面变量的时候,都会先拷贝一份到变量的副本中

        3,在线程中,每一次使用时从变量的副本中获取饿

问题:

        如果A线程修改了堆中共享变量的值

        那么其他线程不一定能及时使用最新的值

Volatile关键字

        强制线程每次在使用的时候,都会看一下共享区域最新的值

public class Money {
    public static volatile int money = 100000;
}

使用只需要在共享数据前加上Volatile关键字即可 

也可以使用Synchronized同步代码块

        1,线程获得锁

        2,清空变量副本

        3,拷贝共享变量最新的值到变量副本中

        4,执行代码

        5,将修改后变量副本中的值赋值给贡献数据

        6,释放锁

四,原子性

所谓的原子性时指在一次 *** 作或者多次 *** 作中,要么所有的 *** 作全部都得到了执行并且不受到任何因素的干扰而打断,要么所有的 *** 作都不执行,多个 *** 作是一个不可分割的整体

coubt++:不是一个原子性 *** 作,也就是在执行的过程中有可能被其他线程打断 *** 作。

public class MyAtom01 implements Runnable{
    private int count = 0;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1,从共享数据中读取数据到本线程栈中
            //2,修改本线程栈变量副本的值
            //3,会把本线程栈变量副本的值赋值给共享数据
            synchronized (this) {
                count++;
                System.out.println("送出了"+count+"个");
            }


        }
    }
}

Volatile关键字:只能保证线程每次使用共享数据的时候是最新指,

                            但是不能保证原子性。

使用Synchronized加锁可以解决代码的原子性,但是效率太低。

1,AtomicInteger原子型类:

JDK1.5之后,使用原子类(Atomiclnteger)来更新共享数据

1,构造方法:

public class AtomicInteger01 {
    public static void main(String[] args) {
        AtomicInteger ac = new AtomicInteger();//初始化一个默认值为0的原子型Integer
        System.out.println(ac);

        AtomicInteger ac2 = new AtomicInteger(10);
        //初始化一个指定值的原子型Integer
        System.out.println(ac2);

    }

2,成员方法:

public class AtomicInteger02 {
    public static void main(String[] args) {
        
        AtomicInteger ac = new AtomicInteger(10);
        int i = ac.get();
        System.out.println(i);

        int andIncrement = ac.getAndIncrement();
        System.out.println(andIncrement);

        int i1 = ac.incrementAndGet();
        System.out.println(i1);

        int i2 = ac.addAndGet(10);
        System.out.println(i2);

        int andSet = ac.getAndSet(19);
        System.out.println(andSet);

        System.out.println(ac.get());
    }
}
 2,使用原子性解决送冰淇凌问题

线程体类

public class MyAtom01 implements Runnable {
    //    private int count = 0;
    AtomicInteger ac = new AtomicInteger(0);
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            int i1 = ac.incrementAndGet();
            System.out.println("送出了" + i1 + "个");
        }
    }
}

测试类

public class Demo01 {
    public static void main(String[] args) {
        MyAtom01 mt = new MyAtom01();
        for (int i = 0; i < 100; i++) {
            new Thread(mt).start();
        }
    }
}
 3,AtomicInteger原理

        自旋锁+ CAS算法

        CAS算法:有3个 *** 作数(内存值V,旧的预期值A,要修改的值B)

                        当旧的预期值A == 内存值  此时修改成功 ,将V改为B

                        当旧的预期值A !=内存值  此时修改失败,不做任何 *** 作

                        并重新获取现在的最新值(这个重新获取的动作就是自旋)

在修改共享数据的时候,把原来的旧值记录下来。如果现在内存中的值跟原来的旧值一样,证明没有其他线程 *** 作过内存值,则修改成功。如果现在内存中跟原来的旧址不一样了,证明已经有其他线程 *** 作过内存值了,则修改失败,需要获取现在的最新值,再次进行 *** 作,这个重新 *** 作的过曾就是自旋。

4,Synchroized和CSA的区别

        相同点:在多线程情况下,都可以保证共享数据的安全性

        不同点:Synchroized总是从最坏的角度出发,认为每次获取数据的时候,别人有可能修改,所以在每次 *** 作共享数据之前,都会上锁(悲观锁)

        csa是从乐观的角度除法,假设每次过去数据别人都不会修改,所以不会上锁,只不过在修改共享数据的时候,会检查一下,别人有没有修改过这个数据

        如果别人修改过,那么我们再次获取现在的最新值

        如果没有修改过,那么就直接修改这个共享数据的值

        (乐观锁)

五,并发工具类

HashMap是线程不安全的(多线程环境下可能出现问题)

为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下

1,Hashtable

        (1),Hashtable采取悲观锁synchronized的形式保证数据的安全性

        (2),只要有线程访问,会将整张表全部锁起来,所以Hashtable的效率底下

2,ConcurrentHashMap

       Map接口:HashMap,Hashtable ,TreeMap, ConcurrentMap 

 ConcurrentHashMap1.8版本底层原理是:哈希表(数组,链表,红黑树结合体)

结合CAS机制+synchronized同步代码块形式保证线程安全

3,CountDownLatch类

方法:

方法解释public  CountDownLatch(int  count);参数传递线程数,表示等待线程数量public  void  await();让线程等待public  void  countDoen();

当前线程执行完毕

运用

等待的资源类

public class MotherThread extends Thread{
    private CountDownLatch cdl;
    public MotherThread(CountDownLatch cdl) {
        this.cdl = cdl;
    }

    @Override
    public void run() {
        try {
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("洗碗");
    }
}

 在执行的资源类

public class ChileThread1 extends Thread{
    private CountDownLatch cdl;
    public ChileThread1(CountDownLatch cdl) {
        this.cdl = cdl;
    }

    @Override
    public void run() {
        for (int i = 1; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "在吃第" + i + "个饺子。");

        }
        cdl.countDown();
    }
}

测试类: 

public class Demo {
    public static void main(String[] args) {
        CountDownLatch cdl = new CountDownLatch(1);

        MotherThread mt = new MotherThread(cdl);
        mt.start();

        ChileThread1 c1 = new ChileThread1(cdl);
        c1.setName("孩子一");

        c1.start();
    }
}

 小结:

        使用场景:让某一条线程等待其他线程执行王碧之后再执行

   CountDownLatch(int  count) :参数写等待线程的数量,并定义一个计数器

    await():让线程等待,当计数器为0时,会唤醒等待的线程

     countDown():线程执行完毕时调用,会将技术器减1;

4,Semaphore类

Semaphore可以设置线程的通行数量

资源类

public class MyRunnable01 implements Runnable{
    private Semaphore Semaphore;
    public MyRunnable01(Semaphore ap) {
        this.Semaphore = ap;
    }
    @Override
    public void run() {
        try {
            Semaphore.acquire();
            System.out.println("获得了通行证。");
            Semaphore.release();
            System.out.println("归还。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试类 

public class Demo01 {
    public static void main(String[] args) {
        Semaphore ap = new Semaphore(2);
        MyRunnable01 mr = new MyRunnable01(ap);
        for (int i = 0; i < 10; i++) {
            new Thread(mr).start();

        }
    }
}

Semaphore   s  = new  Semaphore(参数);

获取管理员对象,参数是最大可执行的线程数

s.acquire();获取通行证的方法

s.release();归还通行证的方法

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5482762.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-12
下一篇 2022-12-12

发表评论

登录后才能评论

评论列表(0条)

保存