Lock锁
死锁现象
线程池
定时器
设计模式
应用场景 : 生产者和消费者问题
假设仓库中只能存放一件产品 , 生产者将生产出来的产品放入仓库 , 消费者将仓库中产品取走消费 .
如果仓库中没有产品 , 则生产者将产品放入仓库 , 否则停止生产并等待 , 直到仓库中的产品被消费者取走为止 .
如果仓库中放有产品 , 则消费者可以将产品取走消费 , 否则停止消费并等待 ,直到仓库中再次放入产品为止 .
1.定义一个资源
2.生产线程
3.消费线程
4.测试类
这时候产生了线程安全问题
1.多线程环境
2.共享数据
3.多个语句操控这个共享数据
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();//唤醒生产线程
}
}
}
}
java的内存模型规定了多有的变量都分配在主存中,但每个线-程都要有自己的工作内存
线程工作内存中所使用到的的变量都是从主存中copy的
线程对所有变量的操作(读取,赋值)必须在工作内存中进行
不同线程之间无法直接访问对方的对方工作内存,线程之间变量的传递都是通过主存来完成的
案例一:解决内存可见性问题
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());
}
}
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());
}
}
}
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();
}
}
方法:
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);
}
}
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));
}
}