实训笔记6.25
- 6.25
- 一、座右铭
- 二、知识回顾
- 2.1 JavaSE
- 2.1.1 基本语法
- 2.1.2 数组
- 2.1.3 JVM内存
- 2.1.4 面向对象
- 2.1.5 Java常用类
- 2.1.6 Java异常机制
- 2.1.7 Java泛型
- 2.1.8 Java集合
- 2.1.9 JavaIO流
- 2.1.10 Java注解
- 2.1.11 Java反射机制
- 2.1.12 Java多线程
- 2.1.13 Java网络编程
- 三、Java多线程
- 3.1 多线程的基本概念
- 3.1.1 程序、进程和线程
- 3.1.2 单核CPU和多核CPU问题
- 3.1.3 并行和并发
- 3.2 Java多线程的问题
- 3.3 Java当中如何创建和启动多线程类
- 3.4 Thread类常用方法
- 3.5 线程的生命周期
- 3.6 线程的数据同步问题--共享数据问题
- 3.6.1 JVM堆区和元数据的数据对于多线程是共享的,堆区如果共享必须满足数据在堆区是唯一的
- 3.6.2 继承Thread类实现的线程和Runnable、Callable实现的线程有什么区别
- 3.6.3 如何解决共享数据的安全性问题
- 3.7 线程的通信问题--线程之间如何交流
6.25
一、座右铭
我的故事你说,我的文字我落,我值几两你定,我去何方我挑。
二、知识回顾
2.1 JavaSE
2.1.1 基本语法
- 标识符、关键字、保留字
- 数据类型
- 变量、常量、字面量
- 运算符
- 控制流程
2.1.2 数组
2.1.3 JVM内存
2.1.4 面向对象
- 类和对象
- 三大特征
- 抽象类、抽象方法、接口
- 常见关键字:package、import、this、super、static、final、instanceof、enum
2.1.5 Java常用类
- 枚举类
- JavaBean类、lombok
- Object类
- 包装类
- String、StringBuffer、StringBuilder
- System类
- Arrays类
- 时间日期类
- Date
- SimpleDateFormat
- Calendar
- Math、Scanner、Random
- Java比较器
2.1.6 Java异常机制
2.1.7 Java泛型
2.1.8 Java集合
- Collection
- List
- Set
- Map
2.1.9 JavaIO流
-
四大基类
-
常用IO流
- 节点流
- 数组流
- 文件流
- 功能流
- 缓冲流
- 转换流
- 对象流
- 打印流
- 标准输入和输出流
- 节点流
-
IO流使用完毕要注意关闭、IO流要配套使用
2.1.10 Java注解
注解的四大元注解
2.1.11 Java反射机制
- Class类
- 通过Class类去获取类中的组成成分
- 通过获取的类中成分去调用相对应的内容
2.1.12 Java多线程
2.1.13 Java网络编程
三、Java多线程
3.1 多线程的基本概念
3.1.1 程序、进程和线程
程序:没有运行起来的代码块
进程:运行起来的程序叫做进程
线程:线程是进程的组成单位,是资源分配的最小单位
3.1.2 单核CPU和多核CPU问题
CPU内核是用来运行线程的最小单位,CPU的一个内核在某一时刻只能运行一个线程。
但是我们电脑上线程的数量是远大于内核数的,也就意味着一个CPU内核同时运行几百个线程。 CPU的调度机制:时间片调度机制
3.1.3 并行和并发
并行:同一时刻多个线程同时运行
并发:同一时刻只能运行一个线程,多个线程抢占时间片运行,给人的感觉就好像是同时运行
3.2 Java多线程的问题
Java的JVM内存本身就是支持多线程运行的,也就意味着Java可以开发多线程程序的。JVM内存分为五部分,Java当中每启动一个线程,会给这个线程启动一个独立的虚拟机栈区和程序计数器,而堆区和元数据区对于多个线程是共享的。
3.3 Java当中如何创建和启动多线程类
1、借助Java中Thread类–Thread类是java.lang包的,是Java中所有多线程类的顶尖父类
2、借助Java中Runnable接口创建多线程
3、借助Java中的Callable接口创建多线程
4、借助Java中的线程池创建多线程
启动多线程只有一种方式:借助Thead线程类中的start方法来启动
启动多线程只有一种方式:
借助Thead线程类中的start方法来启动
代码示例:
/**
* 线程的第一种创建和使用方式:
* 1、继承Thread线程类,并且重写run方法---run方法中就是线程启动起来以后需要执行的业务逻辑
* 2、创建一个这个类的实例对象,然后调用start方法启动多线程
*
* 想要通过Java一个线程输出0-100以内的所有偶数,另外一个线程输出0-100以内的所有奇数
*
* 一个Java程序,没有创建多线程的情况下,有几个线程?
* 1、main线程
* 2、垃圾回收线程
* 3、异常处理线程
* 如果我们要开启多线程,需要在main线程执行过程中开启另外一个线程
* @author 11018
*
*/
public class Demo01 {
public static void main(String[] args) {
// T t = new T();
// t.start();
Thread t = new Thread() {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(i % 2 == 0) {
System.out.println("有一个偶数为:"+i);
}
}
}
};
t.start();
for (int i = 0; i <= 100; i++) {
if(i % 2 == 1) {
System.out.println("有一个奇数为:"+i);
}
}
}
}
class T extends Thread{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(i % 2 == 0) {
System.out.println("有一个偶数为:"+i);
}
}
}
}
/**
* 创建多线程的第二种方式:实现Runnable接口
* 1、实现Runnable接口,然后重写run方法
* 2、创建一个实现类的对象,然后创建一个Thread类对象,把实现类对象当中参数传入到Thread类的构建中
* 3、启动thead类的start方法
* @author 11018
*/
public class Demo02 {
public static void main(String[] args) {
A a = new A();
Thread t = new Thread(a);
t.start();
for (int i = 0; i <= 100; i++) {
if(i % 2 == 1) {
System.out.println("有一个奇数为:"+i);
}
}
}
}
class A implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(i % 2 == 0) {
System.out.println("有一个偶数为:"+i);
}
}
}
}
/**
* 线程的第三种创建方式:Callable接口
* 1、实现类实现callable接口,然后重写call()方法
* 2、创建一个实现类的对象,然后创建FutureTask类包含实现类对象
* 3、创建一个Thread类包含Fuxxxxx 然后调用thread类的start方法开启多线程
* @author 11018
*
*/
public class Demo03 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
B b = new B();
FutureTask<Integer> ft = new FutureTask<>(b);
Thread t = new Thread(ft);
t.start();
for (int i = 0; i <= 100; i++) {
if(i % 2 == 1) {
System.out.println("有一个奇数为:"+i);
}
}
System.out.println(ft.get());
}
}
class B implements Callable<Integer>{
@Override
public Integer call() throws Exception {
for (int i = 0; i <= 100; i++) {
if(i % 2 == 0) {
System.out.println("有一个偶数为:"+i);
}
}
return 0;
}
}
Runnable r = new Runnable() {
@Override
public void run() {
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += i;
}
System.out.println(sum);
}
};
Thread t= new Thread(r);
t.start();
int sum = 1;
for (int i = 1; i <= 5; i++) {
sum *= i;
}
System.out.println(sum);
3.4 Thread类常用方法
Thread类是线程启动运行的核心类,内部提供了很多和线程有关的方法
方法名 | 作用 |
---|---|
run()方法 | Thread类继承的Runnable接口,多线程启动成功以后自动执行的代码方法 |
start()方法 | 开启一个线程,线程一旦开启以后,会自动调用run方法执行其中的业务逻辑 |
stop()方法 | 强制停止一个线程 |
setName(String name) | 给当前线程设置一个别名,如果没有设置,会有一个默认线程名 |
getName() | 获取设置的别名----设置的别名不一定是一个独立的线程 |
static currentThread():Thread | 获取当前程序运行所属的线程 |
static sleep(long timestrap) | 让当前线程睡上一定的毫秒数 |
setPriorit(int num) | 设置线程的优先级,优先级高的更容易获取CPU的执行权 1-10 优先级设置必须在线程开启之前设置 |
yield():void | 让当前线程丧失CPU的执行权,让优先级高的去获取CPU的执行权 |
join():void | 线程加入,将另外一个线程加入到当前线程中,只有当加入的线程执行完成,当前线程才能继续向后执行 |
setDaemon(boolean flag) | 设置 true线程为守护线程 |
isDaemon():boolean | 测试线程是否为守护线程 |
3.5 线程的生命周期
五个阶段:创建、就绪、执行、阻塞、死亡
3.6 线程的数据同步问题–共享数据问题
3.6.1 JVM堆区和元数据的数据对于多线程是共享的,堆区如果共享必须满足数据在堆区是唯一的
3.6.2 继承Thread类实现的线程和Runnable、Callable实现的线程有什么区别
- 继承Thread类线程共享数据很难操作
- 实现方式做的线程本身就是线程共享的
3.6.3 如何解决共享数据的安全性问题
同步代码块
synchronized(同步监视器--锁的钥匙){ 操作共享数据的代码块; }
同步方法
案例:
/**
* 做一个买火车票的程序,三个窗口同时卖100张票
* @author 11018
*
*/
public class Demo01 {
public static void main(String[] args) {
// TrainTicket tt = new TrainTicket();
Window one = new Window();
one.setName("window1");
Window two = new Window();
two.setName("window2");
Window three = new Window();
three.setName("window3");
one.start();
two.start();
three.start();
}
}
-
将操作共享数据的代码块抽取到一个独立的方法中,然后再方法上加上synchronized关键字
代码示例:
public class Demo02 { public static void main(String[] args) { NewWindow nw = new NewWindow(); Thread one = new Thread(nw); Thread two = new Thread(nw); Thread three = new Thread(nw); one.start(); two.start(); three.start(); } } class NewWindow implements Runnable{ int num = 100; @Override public void run() { while(true) { int saleTicket = saleTicket(); if (saleTicket == 0) { break; } } } public synchronized int saleTicket() { if(num > 0) { System.out.println(Thread.currentThread().getName()+"窗口卖出了第"+num+"张票,还剩余"+(--num)+"张票"); return 1; }else { return 0; } } }
-
静态方法 该类的.class实列
-
普通方法 this
-
同步方法再实现接口的线程类中可以实现安全性问题解决,继承体系下实现解决安全性问题比较复杂的。
上锁lock显示的上锁、释放锁:把操作共享数据的代码块放到一个try catch代码块当中,然后再try块显示的上锁 在finally块显示的释放锁
代码示例:
public class Demo03 {
public static void main(String[] args) {
WD w = new WD();
FutureTask<String> ft = new FutureTask<>(w);
FutureTask<String> ft1 = new FutureTask<>(w);
FutureTask<String> ft2 = new FutureTask<>(w);
Thread t = new Thread(ft);
Thread t1 = new Thread(ft1);
Thread t2 = new Thread(ft2);
t.start();
t1.start();
t2.start();
}
}
class WD implements Callable<String>{
ReentrantLock lock = new ReentrantLock();
int num = 100;
@Override
public String call() throws Exception {
while (true) {
try {
lock.lock();
if(num > 0 ) {
System.out.println(Thread.currentThread().getName()+"窗口卖出了第"+num+"张票,还剩余"+(--num)+"张票");
}else {
break;
}
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
return null;
}
}
**死锁:**我要的钥匙被别人用了,别人用的要是我用了
代码示例:
Thread t = new Thread() {
@Override
public void run() {
synchronized (String.class) {
System.out.println("线程正在执行中");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (StringBuffer.class) {
System.out.println("线程的第二个逻辑正在执行");
}
}
}
};
Thread t1 = new Thread() {
@Override
public void run() {
synchronized (StringBuffer.class) {
System.out.println("线程正在执行中");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (String.class) {
System.out.println("线程的第二个逻辑正在执行");
}
}
}
};
t.start();
t1.start();
3.7 线程的通信问题–线程之间如何交流
多个线程之间互相“交流”,多个线程去通过交流去决定谁获得CPU执行,谁阻塞暂停执行
通信是通过Object类的三个方法来完成的:
方法名 | 作用 |
---|---|
wait | 让当前线程失去CPU执行权,进入阻塞状态 |
notify | 唤醒整个进程中通过wait方法进入阻塞状态的线程—优先级最高的被唤醒的概率高一点 |
notifyAll | 唤醒整个进程中所有通过wait方法进入阻塞状态的线程 |
线程通信的方法只能使用在同步代码块或者同步方法中,而且线程通信的三个方法调用者是同步监视器。
线程通信在同步代码和同步方法中,先使用notify/notifyall唤醒wait的线程,然后在代码块的最后再wait让当前线程进入阻塞状态
多线程通信时,多线程使用的同步监视器必须是同一个
代码示例:
package com.sxuek.thread.communication;
/**
* 让两个线程交替打印0-100
* @author 11018
*
*/
public class Demo {
public static void main(String[] args) {
D d = new D();
Thread t1 = new Thread(d);
t1.setPriority(1);
Thread t2 = new Thread(d);
t2.setPriority(5);
Thread t3 = new Thread(d);
t3.setPriority(10);
t1.start();
t2.start();
t3.start();
}
}
class D implements Runnable{
int num = 0;
@Override
public void run() {
while(true) {
synchronized (String.class) {
String.class.notify();
if(num > 100) {
break;
}else {
System.out.printf("%s线程打印了数字%d\n", Thread.currentThread().getName(),num);
num++;
}
try {
String.class.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}