1、线程之间的通信:

多个线程处理同一个资源时,处理的动作是不一样,(大概意思是有相关的先后执行顺序)

2、为什么处理线程之间的通信:

多线线程在执行时,默认是抢夺CPU的执行权,后期看结果,会有所不一样!会造成数据不是我们想要的一个结果
我们想要的是,多个线程有规则的执行。例如:一个线程给变量赋值,一个线程获取变量的值 。

3、通信分析:

一个线程执行时,另外一个线程在等待。直到一个执行完毕之后,另外一个线程才能执行。
Object类中的相关方法
进入到TimeWaiting(计时等待)有两种方式
1.使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
2.使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态
3.使用wait方法, 一直处于等待状态, 除非有一个线程调用notify方法,将该线程唤醒,才能醒过来
唤醒的方法: (锁.方法)
void notify() 唤醒在此对象监视器上等待的单个线程。
void notifyAll() 唤醒在此对象监视器上等待的所有线程

4、生产者与消费者问题分析:

思路:
等待唤醒案例:线程之间的通信
创建一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait 方法,放弃cpu的执行,进入到WAITING状态(无限等待)
创建一个老板线程(生产者):花了5秒做包子,做好包子之后,调用notify方法,唤醒顾客吃包子
注意:
顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行
同步使用的锁对象必须保证唯一
只有锁对象才能调用wait和notify方法
锁.wait :使本线程进入等待状态
锁.notify :唤醒其他线程

5、线程池:

每一个案例都需要创建线程,启动线程! 创建线程和启动线程,都是由系统操作的,跟CPU有关的!只要跟本地系统操作的资源 ,效率都是不高的。程序要跟CPU申请资源,每一次申请,都是需要过程(时间的),要解决这个频繁开启线程的问题,要使用线程池。
如果我们要使用线程就从线程池里拿,用完再放回线程池里边。
好处:
1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

6、线程池的创建:

Executors类中创建线程池的方法:public static ExecutorService newFixedThreadPool(int nThreads)
返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)。

  1. package com.itfxp.thread.thread02;
  2. public class MyRunnable implements Runnable {
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < 10; i++) {
  6. System.out.println(Thread.currentThread().getName() + ": " + i);
  7. }
  8. }
  9. }
  10. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  11. package com.itfxp.thread.thread02;
  12. import java.util.concurrent.ExecutorService;
  13. import java.util.concurrent.Executors;
  14. public class ThreadPool {
  15. public static void main(String[] args) {
  16. // 创建线程池,该线程池中有两个线程
  17. ExecutorService pool = Executors.newFixedThreadPool(2);
  18. // 创建Runnable接口实现类对象
  19. MyRunnable mr = new MyRunnable();
  20. MyRunnable mr1 = new MyRunnable();
  21. MyRunnable mr2 = new MyRunnable();
  22. // new Thread(mr).start();
  23. // 使用ExecutorService中的submit方法进行提交任务
  24. pool.submit(mr);
  25. pool.submit(mr1);
  26. pool.submit(mr2);
  27. // 消毁线程池,一般不消毁,循环使用线程池中的线程
  28. // pool.shutdown();
  29. pool.submit(mr);
  30. }
  31. }

7、voliate关键字:

volatile保证线程间变量的可见性,简单地说就是当线程A对变量X进行了修改后,在线程A后面执行的其他线程能看到变量X的变动,更详细地说是要符合以下两个规则:
==线程对变量进行修改之后,要立刻回写到主内存。==
==线程对变量读取的时候,要从主内存中读,而不是缓存==

  1. /**
  2. * volatile用于保证数据的同步,也就是可见性
  3. */
  4. public class VolatileTest {
  5. private volatile static int num = 0; //如果不加关键字,后续修改的num值是不会被看到的,即线程进入死循环
  6. public static void main(String[] args) throws InterruptedException {
  7. new Thread(
  8. new Runnable(){
  9. public void run(){
  10. while(num==0) { //此处不要编写代码
  11. }
  12. }
  13. }
  14. ) .start();
  15. Thread.sleep(1000);
  16. num = 1;
  17. }
  18. }

8、单例设计模式:

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
特点:
1、单例类只能有一个实例。也就是只有一个对象
2、单例类必须自己创建自己的唯一实例。 写单例构造方法是要私有的
3、单例类必须给所有其他对象,提供一个方法用于获取该对象
前提条件:
1、私有构造方法

2、在本类的成员位置,创建出自己类对象

3、提供公共方法,返回创建的对象 ,该方法必须是静态的
单例模式饿汉式:

  1. /** 单例模式饿汉式*/
  2. public class Single {
  3. private Single(){
  4. }
  5. private static final Single s = new Single();
  6. public static Single getInstance(){
  7. return s;
  8. }
  9. }

单例模式懒汉式:

  1. /** 单例模式懒汉式*/
  2. public class Single {
  3. private Single(){}
  4. private static Single s = null;
  5. public static Single getInstance(){
  6. if(s == null){
  7. s = new Single();
  8. return s;
  9. }
  10. }
  11. }

懒汉式的安全问题:

  1. public class Single {
  2. private Single(){}
  3. private static Single s = null;
  4. public static Single getInstance(){
  5. if(s == null){
  6. synchronized(Single.class){ //单例模式懒汉式是一种对象延迟加载,在多线程情况下会有安全隐患, 要想解决此问题,我们需要加同步代码块,进行解决
  7. if( s == null)
  8. s = new Single();
  9. }
  10. }
  11. return s;
  12. }
  13. }

双层if判断的原因
- 方法中加入同步锁,保证线程安全
- 第二个线程调用方法getInstance()的时候,变量s,已经不是null,被前面的线程new过
- 当已经有对象了,第二个线程没有必要再进入同步了,直接return返回对象即可