javaSE每天练程序——day18线程之间等待唤醒,内存可见性,解决原子性,线程的五大状态,三种线程池,定时器,单例模式

学习目录:

Lock锁
死锁现象
线程池
定时器
设计模式

线程间的等待唤醒机制

线程协作

线程通信

应用场景 : 生产者和消费者问题
假设仓库中只能存放一件产品 , 生产者将生产出来的产品放入仓库 , 消费者将仓库中产品取走消费 .
如果仓库中没有产品 , 则生产者将产品放入仓库 , 否则停止生产并等待 , 直到仓库中的产品被消费者取走为止 .
如果仓库中放有产品 , 则消费者可以将产品取走消费 , 否则停止消费并等待 ,直到仓库中再次放入产品为止 .
javaSE每天练程序——day18线程之间等待唤醒,内存可见性,解决原子性,线程的五大状态,三种线程池,定时器,单例模式_第1张图片
1.定义一个资源
2.生产线程
3.消费线程
4.测试类

  • 这时候产生了线程安全问题
    1.多线程环境
    2.共享数据
    3.多个语句操控这个共享数据

  • 唤醒机制的4个方法
    Object类中
    javaSE每天练程序——day18线程之间等待唤醒,内存可见性,解决原子性,线程的五大状态,三种线程池,定时器,单例模式_第2张图片

  • sleep和wait的区别:
    相同:同样是让线程处于阻塞状态
    不同:sleep需要设定睡眠时间,一旦休眠不释放锁
    wait:可以设定也可以不设定睡眠时间,一旦休眠释放锁

案例一:生产消费的关系

package javaSEreview20190730.线程之间的等待唤醒机制;

/**
 * @Description:TODO
 * @Author:@李小白
 * @Date:2019/7/30 22:30
 */
public class MyTest {
    public static void main(String[] args) {
        Student student = new Student();
        SetThread setStudent = new SetThread(student);
        GetThread getStudent = new GetThread(student);
        setStudent.start();
        getStudent.start();
    }
}

package javaSEreview20190730.线程之间的等待唤醒机制;

/**
 * @Description:TODO
 * @Author:@李小白
 * @Date:2019/7/30 22:29
 */
public class Student {
    public String name;
    public int age;
    public boolean flag=false; //定义一个标记,默认是没有资源了
}

package javaSEreview20190730.线程之间的等待唤醒机制;

/**
 * @Description:TODO
 * @Author:@李小白
 * @Date:2019/7/30 22:29
 */
public class SetThread extends Thread{
    Student student;
    int i=0;
    public SetThread(Student student) {
        this.student=student;

    }

    @Override
    public void run() {
        while (true) {
            synchronized (student){//加把锁,防止数据出现错误
                if (student.flag) {//对于生产者来说,没有资源的就要等待
                    try {
                        student.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }

                if (i%2==0) {
                    student.name="张三";
                    student.age=23;
                }else {
                    student.name="李四";
                    student.age=24;
                }

                //通知消费线程去消费
                student.flag=true;//修改标志
                student.notify();//唤醒消费线程,唤醒之后,多个线程再次开始争抢时间片

                i++;

            }
        }
    }
}

package javaSEreview20190730.线程之间的等待唤醒机制;

/**
 * @Description:TODO
 * @Author:@李小白
 * @Date:2019/7/30 22:30
 */
public class GetThread extends Thread {
    Student student;
    public GetThread(Student student) {
        this.student=student;
    }

    @Override
    public void run() {
        while (true) {//消费线程来一个循环进行接收
            synchronized (student){
                if (!student.flag) {//如果消费线程没有资源的话就要等待
                    try {
                        student.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //如果有资源的话就输出,消费了线程就要通知生产线程去生产
                System.out.println(student.name+"-----"+student.age);
                student.flag=false;//改变标记
                student.notify();//唤醒生产线程

            }
        }
    }
}

多线程(解决内存可见性问题 volatile)(了解)

java内存模型?

  • java的内存模型规定了多有的变量都分配在主存中,但每个线-程都要有自己的工作内存

  • 线程工作内存中所使用到的的变量都是从主存中copy的

  • 线程对所有变量的操作(读取,赋值)必须在工作内存中进行

  • 不同线程之间无法直接访问对方的对方工作内存,线程之间变量的传递都是通过主存来完成的

可见性问题?

  • java提供了volatile关键字
  • 在一个共享变量前加上volatile修饰,他会保证工作内存中修改的值会立即保存到主存中,其他变量读取到的是最新的值,保证变量不会出错.
  • 不加的话,其他线程读取的不是最新的变量,保证不了可见性
  • synchronized和Lock也能够保证可见性,效率太低,他只可以同一个时刻保证一个线程获取锁,然后执行同步代码,在释放锁之前将变量刷新到主存中

volatile 关键字

  • 当多个线程进行操作共享数据时,可以保证内存中的数据可见。
    相较于 synchronized 是一种较为轻量级的同步策略。
  • 但是volatile与其他的锁不同的是
     对于多线程,不是一种互斥关系
     不能保证变量状态的“原子性操作”

案例一:解决内存可见性问题

package javaSEreview20190730.解决内存可见性问题;

/**
 * @Description:TODO
 * @Author:@李小白
 * @Date:2019/7/30 23:16
 */
public class Demo01 {
    public static void main(String[] args) {
        MyRunable myRunable = new MyRunable();
        new Thread(myRunable).start();
        while (true) {
            if (myRunable.getflag()){
                System.out.println("进来了");
                break;
            }
        }
    }
}
class MyRunable implements Runnable{
    volatile boolean flag=false;//解决内存可见性问题
    public boolean getflag(){
        return flag;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(10);//手动制作一个网络延迟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

            flag=true;
            System.out.println("flag的值是"+getflag());

    }
}

多线程(CAS 算法)(了解)解决原子性

  • 原子性?
    不可再被分割
    i++不是原子性
  • CAS算法?
    是一种硬件对并发的支持,解决效率低下的原子性
    是一种无锁的非阻塞算法的实现。
    jdk5增加了并发包java.util.concurrent.*,区别乐观锁(volatile和CAS算法,无互斥性)和悲观锁(synchronized,有互斥性一个线程必须操作完)
    案例一:解决原子性
package javaSEreview20190730.解决原子性问题;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description:TODO
 * @Author:@李小白
 * @Date:2019/7/31 0:03
 */
public class Demo {
    public static void main(String[] args) {
        MyRunable myRunable = new MyRunable();
        for (int i = 0; i < 10; i++) {
            new Thread(myRunable).start();
        }
    }
}
class MyRunable implements Runnable{

    //保证了原子性,   把普通变量换成原子变量,
    AtomicInteger atomicInteger= new AtomicInteger(1);

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);//给一个网络议延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"----"+atomicInteger.getAndIncrement());
        }
    }
}

线程的五大状态

javaSE每天练程序——day18线程之间等待唤醒,内存可见性,解决原子性,线程的五大状态,三种线程池,定时器,单例模式_第3张图片

匿名内部类的开启线程

package javaSEreview20190730.匿名内部类开启线程;

/**
 * @Description:匿名内部类开启线程
 * @Author:@李小白
 * @Date:2019/7/31 0:21
 */
public class Demo {
    public static void main(String[] args) {
        //方式一:第一种方式
        new Thread(){
            @Override
            public void run() {
                System.out.println("方式一的第一种方式执行了");
            }
        }.start();


        Thread th=new Thread() {
            @Override
            public void run() {
                System.out.println("线程执行了");
            }
        };
        th.start();
        //方式二:
        new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("第二种方式执行了");
            }
        }).start();

    }
}

线程池

  • 定义:一个装有一定线程对象的容器djk5之后出现线程池
  • 因:手创建线程耗费资源,循环后死亡
    虽然线程是轻量级的但是多线程的开启让服务器受不了
    多线程之间的切换让cpu受不了
    所以需要一个少线程,cpu繁忙的线程池

JDK5新增了一个Executors工厂类来产生线程池

方法:
Executors.下面的三个方法
public static ExecutorService newCachedThreadPool(): 根据任务的数量来创建线程对应的线程个数
submit()创建线程
shutdown()关闭线程
public static ExecutorService newFixedThreadPool(int nThreads): 固定初始化几个线程
public static ExecutorService newSingleThreadExecutor(): 初始化一个线程的线程池

案例一:方法一应用

package day20190801.线程池;

import day20190728.study01.线程的创建方式二.Runables;

/**
 * @description:
 * @author: @李小白
 * @create: 2019-08-01 10:18
 */
public class MyRunable extends Runables {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程执行了");
    }
}

package day20190801.线程池;

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

/**
 * @description: 线程池,根据任务的个数来创建线程的个数,可以创建多个线程池
 * @author: @李小白
 * @create: 2019-08-01 10:19
 */
public class CachedThreadPoll {
    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();//选择创建多个线程,和任务相匹配的多个线程
        Future submit = cachedThreadPool.submit(new MyRunable());//新建线程
        Future submit2 = cachedThreadPool.submit(new MyRunable());//新建线程
        Future submit3 = cachedThreadPool.submit(new MyRunable());//新建线程
        Future submit4 = cachedThreadPool.submit(new MyRunable());//新建线程
        cachedThreadPool.shutdown();//关闭线程池
    }
}

案例二:方法二应用

MyRunable方法不变 
package day20190801.线程池;

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

/**
 * @description: 创建指定的线程的个数
 * @author: @李小白
 * @create: 2019-08-01 10:29
 */
public class FixedThreadPoll {
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);//指定了3个线程
        Future submit = fixedThreadPool.submit(new MyRunable());
        Future submit2 = fixedThreadPool.submit(new MyRunable());
        Future submit3 = fixedThreadPool.submit(new MyRunable());
        fixedThreadPool.shutdown();//关闭线程池
    }
}

案例三:方法三的应用

MyRunable不变
package day20190801.线程池;

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

/**
 * @description: 只有一个而单独的线程
 * @author: @李小白
 * @create: 2019-08-01 10:34
 */
public class SigleThreadPool {
    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        Future submit = singleThreadExecutor.submit(new MyRunable());
      
        singleThreadExecutor.shutdown();
    }
}

案例四:
用Callbale的方式执行线程池

package day20190801.线程池;

import java.util.concurrent.Callable;

/**
 * @description: 第三种方式
 * @author: @李小白
 * @create: 2019-08-01 10:57
 */
public class MyCallable implements Callable {
    int num;
    int sum=0;
    public MyCallable(int num){
        this.num=num;
    }
    @Override
    public Integer call() throws Exception {
        
        for (int i = 0; i < num; i++) {
            sum+=i;
        }
        return sum;
    }
}

package day20190801.线程池;

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

/**
 * @description:
 * @author: @李小白
 * @create: 2019-08-01 10:58
 */
public class MyCallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        MyCallable myCallable = new MyCallable(10);//指定10个任务
        Future submit = fixedThreadPool.submit(myCallable);
        Integer integer = submit.get();
        System.out.println(integer);

        MyCallable myCallable2 = new MyCallable(100);//指定10个任务
        Future submit2 = fixedThreadPool.submit(myCallable2);
        Integer integer2 = submit.get();//获取线程执行完之后的返回结果
        System.out.println(integer2);


    }
}

多线程(定时器的概述和使用)(理解)

  • 概念:调度多个执行任务以后台执行的方式执行的方式执行
  • 在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。
    Timer:
    public Timer()
    public void schedule(TimerTask task, long delay):
    public void schedule(TimerTask task,long delay,long period);
    public void schedule(TimerTask task, Date time):
    public void schedule(TimerTask task, Date firstTime, long period):
    TimerTask:定时任务
    public abstract void run()//继承TimerTask后写的run
    public boolean cancel()//直接取消定时器
    案例一:
    定时关机
package day20190801.定时器;

import sun.awt.TimedWindowEvent;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @description: 定时关机
 * @author: @李小白
 * @create: 2019-08-01 11:16
 */
public class Demo1定时关机 {
    public static void main(String[] args) throws ParseException {
        Runtime runtime = Runtime.getRuntime();
        Timer timer = new Timer();
        String data="2019-08-01 11:31:00";
        timer.schedule( new TimerTask() {
            @Override
            public void run() {
                try {
                    runtime.exec("shutdown -s -t 0");//定时关机的Dos命令
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        },new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(data));
    }
}

案例二:方法三和方法四
删库跑路

package day20190801.定时器;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @description: 在指定的日期进行删库跑路
 * @author: @李小白
 * @create: 2019-08-01 11:24
 */
public class Demo02删库跑路 {
    public static void main(String[] args) throws ParseException {
        Timer timer = new Timer();
        String data="2019-08-01 11:31:00";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date parse = simpleDateFormat.parse(data);
        timer.schedule(new TimerTask() {
           @Override
           public void run() {
               System.out.println("删库跑路");
           }
       },parse);
    }
}

案例三:
定时删除文件夹

package day20190801.定时器;

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @description:
 * @author: @李小白
 * @create: 2019-08-01 11:34
 */
public class MyTimerTask03 extends TimerTask {
    Timer timer;
    public MyTimerTask03(Timer timer){
    this.timer=timer;
    }
    @Override
    public void run() {
        File file = new File("D:\\图片");
        getDelete(file);
    }

    private void getDelete(File file) {
        File[] files = file.listFiles();
        file.delete();//如果是空文件就直接删除
        for (File f : files) {
            if (f.isFile()){
                f.delete();
            }else {
                getDelete(f);
            }
        }
    }
}

package day20190801.定时器;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;

/**
 * @description:
 * @author: @李小白
 * @create: 2019-08-01 11:33
 */
public class Demo03定时删除文件夹 {
    public static void main(String[] args) throws ParseException {
        Timer timer = new Timer();
        String data="2019-08-01 11:31:00";
        Date simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(data);
        MyTimerTask03 myTimerTask1 = new MyTimerTask03(timer);
        timer.schedule(myTimerTask1,simpleDateFormat);

    }
}

案例五:方法一和方法二的应用
连环爆炸

package day20190801.定时器;

import java.util.Timer;
import java.util.TimerTask;

/**
 * @description:
 * @author: @李小白
 * @create: 2019-08-01 11:12
 */
public class Demo04连环爆 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("爆炸了!!!!");
            }
        };
        //2秒后爆炸
        timer.schedule(timerTask,1000,2000);//间隔1000毫秒就爆炸一下
//        timer.cancel();//直接就取消定时
    }
}

多线程(多线程常见的面试题)(理解

A:多线程有几种实现方案,分别是哪几种? 三种
B:同步有几种方式,分别是什么? 三种 同步代码块 同步方法,Lock
C:启动一个线程是run()还是start()?它们的区别?

设计模式(设计模式的概述和分类)(了解)

单例设计模式:单例:在内存中,只存在该类的一个对象
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编写、代码设计经验的总结。

使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性以及代码的结构更加清晰.
B:设计模式分类
创建型模式(创建对象的): 单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
行为型模式(对象的功能): 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
结构型模式(对象的组成): 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

设计模式(单例模式之懒汉式、饿汉式)(掌握)

饿汉式和懒汉式的区别:
开发中 饿汉式
面试中 懒汉式
面试的两种思想
线程安全思想
延迟加载思想

案例一:
饿汉式

package day20190801.单例模式;

/**
 * @description:
 * @author: @李小白
 * @create: 2019-08-01 13:44
 */
public class Student {
    private static Student student=null;

    public Student() {
    }

    public synchronized static Student getStudent(){
        if (student==null){
            Student student = new Student();
        }
        return student;
    }
}

package day20190801.单例模式;

import com.sun.xml.internal.ws.util.pipe.DumpTube;

/**
 * @description:汉模式
 * @author: @李小懒白
 * @create: 2019-08-01 13:46
 */
public class Demo01 {
    public static void main(String[] args) {
        Student student = Student.getStudent();
        Student student1 = Student.getStudent();
        System.out.println(student.equals(student1));
    }
}

案例二:
懒汉式:

package day20190801.单例模式;

/**
 * @description:
 * @author: @李小白
 * @create: 2019-08-01 14:22
 */
public class Teacher {
    public static Teacher teacher=new Teacher();

    public Teacher() {
    }

    public static Teacher getTeacher(){

        return teacher;
    }
}

package day20190801.单例模式;

/**
 * @description:
 * @author: @李小白
 * @create: 2019-08-01 14:22
 */
public class Demo02 {
    public static void main(String[] args) {
        Teacher teacher = Teacher.getTeacher();
        Teacher teacher2 = Teacher.getTeacher();
        System.out.println(teacher.equals(teacher2));
    }
}

你可能感兴趣的:(javaSE每天练程序——day18线程之间等待唤醒,内存可见性,解决原子性,线程的五大状态,三种线程池,定时器,单例模式)