1.常用类
1.1Date
Date专门处理日期
这个类即将被弃用,官方手册里面说会有其他类代替 Calendar类来代替
import java.util.Date;
public class Demo1 {
public static void main(String[] args) {
//1.实例化Date对象
Date date = new Date();
//Tue Aug 17 09:37:55 CST 2021 这个时间是不友好的
System.out.println(date);
Date date1 = new Date(2000, 10,5);//已经被弃用
System.out.println(date1);
//Tue Aug 17 09:42:02 CST 2021
//System.currentTimeMillis() 时间戳 1970以后的
Date date2 = new Date(System.currentTimeMillis());
System.out.println(date2);
System.out.println(date2.getYear() + 1900);
System.out.println(date2.getMonth() + 1);//0代表1月 7月代表8月
}
}
1.2Calendar类
专门出来时间日期的一个类
Calendar是一个抽象类,不能直接实例化,但是官方手册告诉我们如何把对象创建出来
Calendar
的getInstance
方法返回一个Calendar
对象,其日历字段已使用当前日期和时间进行初始化:
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class Demo5 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(calendar.YEAR));
System.out.println(calendar.get(calendar.MONTH)+1);//因为在封装的时候 从0开始的,所以月份需要加1
System.out.println(calendar.get(calendar.DATE));
System.out.println(calendar.get(calendar.DAY_OF_MONTH));
System.out.println(calendar.get(calendar.DAY_OF_WEEK)-1);//老外是将周日看成咱们的周一,所以中国来算的话需要减一
System.out.println(calendar.get(calendar.DAY_OF_YEAR));
System.out.println(calendar.get(calendar.HOUR));//默认12小时计时法
System.out.println(calendar.get(calendar.MINUTE));
System.out.println(calendar.get(calendar.SECOND));
Date date = calendar.getTime();//获取时间
System.out.println(date); //英文的很不友好 看不懂
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
System.out.println(simpleDateFormat.format(date));
}
}
常用类不常用,但是学习这些东西的时候,要学习思想,假如给你一个你没有见过的类,
你要从几点考虑:
1.这个类是干嘛?比如String处理字符串 Date处理日期时间 System是和系统有关的。专门的类做这一类相关的事儿
2.看这个类的构造方法,能不能new?
3.这个类下面java估计猜测应该封装了好多方法。要学习常用的方法
2.线程和进程【重点】
面试必问!!!
3.1什么是进程
独立运行的应用程序叫进程
什么是应用程序,就是咱们打开各个软件,比如 谷歌浏览器,360,qq音乐等这些软件,他们都是独立的一个运行起来的软件,叫应用应用程序,
代码里面写过Demo1 Demo2这些类,也可以叫一个应用程序,也可以叫进程
进程需要通过系统的分配,获取系统中的cpu,内存,显卡,网络等。
独立性 :qq和谷歌浏览器没有关系的,他们之间是独立的
互斥性:咱们软件的运行都是在抢占cpu,谁抢过谁先执行。
直到为什么有的时候一个软件会卡,让你等待。是因为这个软件在抢占cpu
的时候,没有抢占过别的软件。所以要么关闭软件,等待响应。
3.2什么是线程
进程是由多个线程组成的,而且一个进程至少得有一个线程
线程是进程的灵魂所在,看不见摸不着的。进程可以看到,在执行的这个应用程序
,但是内存是怎么运行的呢,其实是线程在运行的
线程是组成进程的最小单位。
一个人比作一个进程,那么细胞就是线程
线程具有什么特性:
1.抢占式运行
cpu执行应用程序的时候按照时间片执行的,而且单位的时间片线程是相互抢占式的运行。
厕所就三个坑位,但是学校学生300个,抢占这个坑位。谁抢到以后就先用,等你释放以后,其他人再抢。线程 很累,不断在抢占cpu,不断的释放。
2.资源共享性
一个线程可以共享当前资源,CPU 内存。
qq这个进程,比如我占用的运行内存3MB,多个线程可以共同去抢占这3MB的运行内存。
进程之间能资源共享?线程之间能资源共享吗?
进程之间不能资源共享 idea 和qq这是两个进程,可以共享资源?不可以
线程之间可以的,在一个应用程序中,抢占的同一个资源
至少有几个线程?
2个线程一个是main主函数线程,另外一个JVM垃圾回收线程。
3.3进程和线程区别【面试题】
进程是一个完整的应用程序,而且一个进程至少有一个线程,可以把进程看做一个生产车间。线程是进程中一个执行的功能,可以看成一个生产车间的一条生产线。
一个生产车间是由多个生产线组成的。一个应用程序是由多个线程组成的。
进程申请是windows系统的资源
线程申请的是进程的资源
多个线程在执行的时候,cpu会根据每个线程的分配时间片来随机轮流执行。
每个线程最多占用的时间片大概是20ms,过了这个时间片就切换到另外一线程了。
厕所比作一个应用程序,一个进程。
在这个进程中,有300个(学生)线程。去申请这个厕所的资源有3MB(3个坑位)。
每个学生抢占这个坑位,只给你留的时间片大概20ms。保证这个进程一致在执行,
没有挂掉,视觉是感应不到的他在抢占,你感觉咱们应用程序是一直在执行的。
3.4并发和并行
并发:同时发生,轮流交替来执行的。
并行:真正意义上的同时执行
举个例子:
比如你点了两盘菜,最终你都要将这两盘菜吃完,。
从老板的角度有来看。属于同时吃完的。但是严格意义上来讲,轮流交替吃的。属于并发。
并行:比如你和你同桌两个人,一人点了一个菜,你吃你的我吃我的,并行
3.5线程的优缺点
优点:
1.提高资源的利用率,cpu不至于闲着
2.电脑可以执行多个功能
3.提升用户的体验
劣势:
1.加重cpu的负担
2.降低CPU执行其他线程的概率,会导致咱们的软件的卡顿
3.共享资源的问题【重点!!!】
4.死锁的情况【避免】
3.6创建线程的两种方式
Thread
方式1: 不用!!!
1.自定义线程类去继承Thread类。重写run方法
2.在主函数中创建实例,调用start()方法开启线程
class Thread1 extends Thread{
@Override
public void run() {
System.out.println("我是Thread1线程");
}
}
public class MyThread {
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
thread1.start();
System.out.println("我是main主线程");
}
}
方式2:【一般开发中使用】
1.自定义一个线程类,去实现Runnable接口,重写run方法
2.实例化线程类,这实例化好的类对象会当成一个参数传给 Thread这个类
class MThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我是MThread线程 编号:"+i);
}
}
}
class MThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("人家是MThread1线程啦 编号:"+i);
}
}
}
public class Thread2 {
public static void main(String[] args) {
Thread thread = new Thread(new MThread());
thread.start();
Thread thread1 = new Thread(new MThread1());
thread1.start();
for (int i = 0; i < 100; i++) {
System.out.println("我是main主线程 编号:"+i);
}
}
}
同时也可以看到线程是抢占式来获取资源的,MThread线程执行到31号就变成MThread1线程执行了
3.7线程类常用的方法
构造方法:
Thread();
创建了一个线程对象
Thread(Rnnable target);
创建线程对象,传入的的是Runnable接口的实现类
Thread(Rnnable target, String name);
创建线程对象,传入的的是Runnable接口的实现类,并给当前线程起一个名字
成员方法:
static Thread currentThread();
获取当前线程对象的
void setName(String name);
设置线程的名字
String getName();
获取线程的名字
void setPriority(int newPriority);
设置当前线程的优先级,优先级只能增加线程的执行概率,不一定的优先
线程默认的优先级是5,取值范围是1~10,10s是最高的,1的优先级最低 优先级只是调高了线程的概率,并不是按照优先级执行的,线程还是抢占式运行的
int getPriority();
获取当前线程的优先级
static void sleep();让当前线程进行休眠的,休眠的传入的是一个毫秒数
class MyThread2 implements Runnable{
@Override
public void run() {
Thread thread = Thread.currentThread();//获取当前线程对象
thread.setName("张三");//给当前线程赋名
System.out.println(thread.getPriority());//获取当前线程优先级 默认值为5
thread.setPriority(8);//设置线程的优先级
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(100);//设置线程休眠时间 注意在run方法内使用sleep方法不能进行抛出异常 只能Try-catch
System.out.println(thread.getName()+i);//thread.getName 获取当前线程的名字
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyThread2());
thread.start();
for (int i = 0; i < 50; i++) {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+i);
}
}
}
3.8 关于sleep方法异常问题
class MyThread1 implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
//以下的这个代码只有一个try-catch,没有throws
/**
* 是因为重写,开启了严格模式,
* 重写要求子类和父类的方法必须保持一致
* 在Runnable接口中,run方法后面没有throws没有抛,
* 所以子类也不能抛
*
* 在以后的开发中会遇到这种情况,得想通,为啥不能抛,只能try-catch
*
* */
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Demo2 {
public static void main(String[] args) {
}
}
3.9 同步代码块【难】
用来处理线程共享资源问题的。
共享机器:ATM机器,一个人在办理业务的时候,你进去锁门,锁住这个资源,其他人需要排队
厕所,三个坑位,我占着一个坑位,之后,后面得排队。如果不排队的话,
好几个人都抢到这个资源了,意味着一个坑位有好几个人在用!!!乱套了!!!!
就是线程不安全的情况!!!
看电影需要买电影票:
《扫黑风暴》 【共享资源】
网络:淘票票 美团 猫眼
这里有三种订票方式。需要卖出去100张,通过三个渠道去卖这100张票,
100张票就是这三个渠道的共享资源,如果把三个渠道比作三个线程,这三个线程要去共享100张票。
卖票 ticket = 100
淘票票 第 99
美团 第99
猫眼 第99
线程的不安全性!!!
加锁!!!线程的同步
问题1:
这100张票该怎么保存,选择什么数据类型?
int 类型的数据
成员变量?创建三个线程,如果ticket是一个成员变量的话,
每一个销售线程ticket变量,ticket这个变量他就不是共享资源了。
局部变量?定义在方法体内,定义在run方法中,每一次运行run方法,ticket都要重新定义一次,运行之后又销毁了,没有可持续性。
类变量?静态变量?可以
静态的成员变量,保存在数据区。可以提供给当前类对象使用,而且一处修改,处处修改!!!
class MovieTicket implements Runnable{
private static int ticketNum = 100;
@Override
public void run() {
while (true) {
synchronized ("锁") {
if (ticketNum > 0) {
System.out.println(Thread.currentThread().getName() + ticketNum);
ticketNum--;
} else {
System.out.println("卖完了!");
break;
}
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
Thread thread1 = new Thread(new MovieTicket(),"美团");
Thread thread2 = new Thread(new MovieTicket(),"猫眼");
Thread thread3 = new Thread(new MovieTicket(),"淘票票");
thread1.start();
thread2.start();
thread3.start();
}
}
不加锁的时候会出现卖同一张电影票的情况,会导致线程不安全。
加锁的话这种情况就可以得到解决,因为加过锁之后锁内的代码块只有一个线程在执行,执行完再下一个执行,所以就不会出现卖同一张票的情况啦
不加锁,会导致资源不同步,线程不安全性。
加完锁以后,会解决线程不安全的问题。但是你的执行效率比不加锁的时候低
追求安全,不要追求效率了
卖火车票一样。
下去可以看看这个博客,多学点!!!
Java 多线程详解(三)------线程的同步 - YSOcean - 博客园
理解至上,代码可以不会写,但是线程怎么走的必须得知道
3.10 守护线程
例如:
qq,内网通这些聊天软件,主程序叫非守护线程。而所有的聊天窗口线程叫守护线程。
如果主程序关闭,聊天窗口也就会关闭了。
主线程挂了,那么守护线程也随即挂了。
jvm,垃圾回收线程就是守护线程。当一个应用程序执行完毕,gc这个线程就停止了
守护线程就好比一个小喽喽,他的生死无关紧要,依赖整个主线程而运行的。但是起到的作用很大。
好比古代,皇帝死了以后,会有陪葬的。这个陪葬的就是守护线程
以后开发中,软件更新。日志更新,都是需要用到守护线程的。
3.11 死锁
面试的时候会问!!!开发的时候千万别写死锁!!!
死锁是一种bug的存在
1.什么是死锁
2.死锁有什么危害
3.代码实现一个必然的死锁。
3.11 什么是死锁
关键词:并发场景,多线程, 互不相让
死锁一定发生在并发场景,尤其是锁,目的是为了线程安全,物极必反。
死锁是一种状态,当两个线程互相持有对方的资源的时候,却又不主动释放自己手中的资源,导致大家都用不了了。线程无法继续往下执行。被称为死锁!!!
写出一个死锁【java代码】
class DeadLocking implements Runnable {
private boolean flag;
private Object object1;
private Object object2;
public DeadLocking(boolean flag, Object object1, Object object2) {
this.flag = flag;
this.object1 = object1;
this.object2 = object2;
}
@Override
public void run() {
if (flag) {
synchronized (object1) {
System.out.println("我是" + Thread.currentThread().getName() + "获得的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我准备获得Thread-1的锁");
synchronized (object2) {
System.out.println(Thread.currentThread().getName() + "获得了全部锁");
}
}
}
if (!flag) {
synchronized (object2) {
System.out.println("我是" + Thread.currentThread().getName() + "获得的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我准备获得Thread-0的锁");
synchronized (object1) {
System.out.println(Thread.currentThread().getName() + "获得了全部锁");
}
}
}
}
}
public class Demo3 {
public static void main(String[] args) {
Object object1 = new Object();
Object object2 = new Object();
DeadLocking deadLocking1 = new DeadLocking(true, object1, object2);
DeadLocking deadLocking2 = new DeadLocking(false, object1, object2);
Thread thread1 = new Thread(deadLocking1);
Thread thread2 = new Thread(deadLocking2);
thread1.start();
thread2.start();
}
}
可以看到程序一直在运行,没有停止
以上的代码看看就行了,千万不要模仿着去写,开发中这种情况是禁止的,不能写死锁
线程1锁lock1 但是想申请lock2。这个时候线程2已经获得了lock2这个锁对象了,在释放lock2之前得获取lock1对象,形成了一个闭环,陷入死锁循环
找了开锁公司,给你卡一个箱子。但是开锁需要证明身份。但是你证明身份的东西在箱子里面。 开锁公司:你先证明你的身份
你:证明身份东西在箱子里面,你打开之后我就能证明
开锁公司:有规定,你必须先证明你身份。
死锁!!!
3.12 线程的生命周期
1.创建线程
2.线程开启
3.可运行状态
4.运行状态
5.中间有可能阻塞 sleep() 死锁 只要你线程执行不下去,都算阻塞了。
6.线程的销毁
声明周期,就好比一个人的人生。
人先生出来,(人吃奶长大,上学,不挣钱)可运行状态,(工作 挣钱)运行状态
(失业了挣不了钱了)阻塞 ,人挂了。这个人的一生
3.13 线程的三个重要的方法
是Object下面的方法
wait()
调用该方法的线程处于等待状态【阻塞状态】,需要使用对象调用这个方法
而且这个对象必须是锁对象,wait方法一般和锁一起使用
notify()
唤醒线程,需要通过对象来调用,而且这个对象是锁对象
唤醒一个线程
notifyAll()
唤醒多个线程
Message类
public class Message {
private String message;//声明一个类,然后这个类实例化以后,当成一个锁对象看待
public Message(){}
public Message(String message){
this.message=message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Waitter类 和 Waitter2类
import java.text.SimpleDateFormat;
public class Waitter implements Runnable{//一个线程,这个线程有阻塞,需要使用noitify来唤醒的线程
private Message message;
public Waitter(Message message) {
this.message = message;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (message){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
System.out.println(name+" 获得锁的时间是:"+simpleDateFormat.format(System.currentTimeMillis()));
System.out.println("=====================");
try {
message.wait();//线程走到这个地方,突然阻塞了,这个线程,突然暂停。
//这个wait方法 是需要另外一个线程来唤醒他,唤醒他以后,再继续执行下面的代码
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+"起床啦!!!"+message.getMessage());
}
}
import java.text.SimpleDateFormat;
public class Waitter2 implements Runnable{
private Message message;
public Waitter2(Message message) {
this.message = message;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (message){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
System.out.println(name+" 获得锁的时间为:"+simpleDateFormat.format(System.currentTimeMillis()));
System.out.println("=====================");
try {
message.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+"起床啦!!!"+message.getMessage());
}
}
Notify类
public class Notify implements Runnable{
private Message message;
public Notify(Message message) {
this.message = message;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
try {
Thread.sleep(10000);//让线程2休眠10s 目的为了等待线程1进入等待状态
synchronized (message){
//message.notify();
//唤醒了另外一个线程,唤醒的是随机的
message.notifyAll();
//message.notifyAll();方法是把所有wait的线程都唤醒
System.out.println(name+" 直接唤醒一波!");
System.out.println("====================");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Client类
public class Client {
public static void main(String[] args) {
Message message = new Message("我是你爹");
Waitter waitter = new Waitter(message);
Waitter2 waitter2 = new Waitter2(message);
Notify notify = new Notify(message);
Thread thread1 = new Thread(waitter,"线程1");
Thread thread2 = new Thread(waitter2,"线程2");
Thread thread3 = new Thread(notify,"线程3");
thread1.start();
thread2.start();
System.out.println("====================");
thread3.start();
/**
*
* 好比:
* 你是一个等待线程:写的有wait方法
* 本来在上线下课,疫情了,会发生阻塞,接到政府通知,不能出门
* 政府是一个唤醒线程:写了notify方法
* 说疫情结束了,小区解封,给你一个通知,我才能出门
*
* 总结:最起码的两个线程:一个等待线程 wait()
* 另外一个肯定是唤醒线程 notify()
* 这两个方法必须一起使用,不然你写的代码毫无意义,代码不执行了
*
* */
}
}
大家也可以看浏览一下这篇博客详细了解锁