1.线程和进程的区别
进程:由系统资源分配和调度的最小单元,而线程依赖于进程存在,程序执行的最小单位!
-
如果创建线程----必须有进程----->创建系统资源
-
Java语言不能够创建系统资源, 借助于底层语言C语言来操作
-
Java提供类Thread类
-
借助于Thread类如何实现 线程的创建?
-
并发:在一个时间点同时发生
-
并行:在一个时间段内同时发生
-
开发步骤:
1)自定义一个类 继承自 Thread(线程 是程序中的执行线程。Java 虚拟机允许应
用程序并发地运行多个执行线程。 )
2)该子类应重写 Thread 类的 run 方法
3)创建当前子类对象,然后启动线程:start()而非run方法()
-
启动线程:使用start 方法
2线程的成员方法:
线程实现方式1
设置线程名称:public final void setName(String name)
获取线程名称:public final String getName()
public final void join()
throws InterruptedException :等待该线程终止
public static void main(String[] args) {
//创建两个线程
ThreadJoin tj1 = new ThreadJoin() ;
ThreadJoin tj2 = new ThreadJoin() ;
ThreadJoin tj3 = new ThreadJoin() ;
//设置名称
tj1.setName("令狐冲") ;
tj2.setName("东方不败") ;
tj3.setName("任盈盈") ;
tj1.start();
//try...catch...捕获异常
//throws:抛出异常:方法声明上
try {
tj1.join(); //等待tj1终止
} catch (InterruptedException e) {
e.printStackTrace();//日志: 跟踪堆栈,将异常信息打印在控制台上
}
tj2.start();
tj3.start();
}
public static void yield():暂停当前正在执行的线程,执行其他线程!
3.Thread类的常量字段表
- public static final int MAX_PRIORITY 10
- public static final int MIN_PRIORITY 1
- public static final int NORM_PRIORITY 5 :默认值
- public final void setPriority(int newPriority)设置优先级
- public final int getPriority()
- 优先级越大的线程,抢占CPU的执行权(cpu一点点时间片,可以高效的进行切换)的资格大
- 优先级越小的线程:抢占CPU的执行权越小!
- 线程的执行具有随机性!
public static void main(String[] args) {
//创建三个线程类对象
ThreadPriority tp1 = new ThreadPriority() ;
ThreadPriority tp2 = new ThreadPriority() ;
ThreadPriority tp3 = new ThreadPriority() ;
tp1.setName("高圆圆");
tp2.setName("赵又廷");
tp3.setName("张佳宁");
//设置优先级
tp1.setPriority(10);
tp2.setPriority(1);
System.out.println(tp3.getPriority());//5
//启动线程
tp1.start();
tp2.start();
tp3.start();
}
1.成员实现方式2
Runable(实现关系)
步骤:
(1)自定义一个类,实现Runable接口,重写run方法
(2)在用户线程(main)创建当前“资源类”对象
然后创建Thread类对象,将“资源类”对象作为参数传递
public Thread(Runable target ,String name)
(3)分别启动线程
//thread类的静态功能
public static Thread currenThread()表示正在执行的线程对象的引用
电影院有三个窗口,总共100张票,使用多线程模拟!
*
* 实现方式2
* 使用Runnable接口的方式
*
*
* 第二种方式能够体现 "资源共享"
*
* 程序出现:
* 1)一个张票可能被卖多次(出现同票)
* 线程的执行具有随机性(原子性操作:最简单,最基本的操作语句:- -,++...)
* 2)出现了负票
* 线程的延迟性导致(加入睡眠100毫秒)
* 现在的程序存在 "多线程安全问题"
*/
public class SellTicketDemo {
public static void main(String[] args) {
//创建资源共享类对象
SellTicket st = new SellTicket() ;
//创建多个线程类对象,将资源共享类作为参数传递
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//启动线程
t1.start();
t2.start();
t3.start();
}
}
2.synchronized:关键字---- 内置语言实现的(jvm来实现锁定操作: 锁的释放:自动释放)
-
可以是通过代码块(代码中),可以在方法上使用(同步方法)
-
synchronized同步锁---属于"悲观锁"
-
悲观锁:自己线程本身在执行的时候,其他线程不能进入同步代码块中,其他线程不能进入
-
修改数据,保证数据的安全性! (针对频繁的写入操作)
public class SellTicket implements Runnable {
public static int tickets = 100 ;//100张票
private Demo demo = new Demo();
// private Object obj = new Object() ;
//t1,t2,t3
@Override
public void run() {
//为了模拟一直有票
while(true){
//t1,t2,t3
//加入同步代码块
//把它理解为:门的开/关
// synchronized(new Object()){ //三个线程有自己的锁对象
//t1进来,t2和t3没有办法进入的
// synchronized(obj){ //一个锁对象
//锁对象可以是任意的Java类对象
synchronized(demo){ //一个锁对象
if(tickets>0){ //t1进来
//睡眠100毫秒
//为了模拟网络延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"正在第"+(tickets--)+"张票");
}
}
}
}
}
现在的程序虽然能够体现"资源共享",程序出负票和同票,如何解决安全问题?
-
校验多线程安全问题的标准是什么?
-
1)查看当前程序是否是多线程环境 是
-
2)是否存在共享数据 是 tickets
-
3)是否有多条语句对共享数据进行操作 存在
1),2)都必须具备:现在使用的就是多线程环境,而且存在共享数据
从3)入手
Java提供一个同步机制:将多条语句对共享数据操作的使用同步代码块包括起来
格式:
synchronized(锁对象){ //多个线程必须使用的同一把锁 (可以任意的Java类对 象)将多条语句对共享数据操作}
3.同步方法
//如果一个方法进来就是一个同步代码块---->
// 可以将synchronized抽取到方法声明上:同步方法 (非静态)
//同步方法:它的锁对象:this 当前类对象的地址值引用
public class SellTicket implements Runnable {
//100张票
public static int tickets = 100 ;
// public Object obj = new Object() ;
//定义一个统计变量
int x = 0 ;
@Override
public void run() {
//模拟一直有票
while(true){
if(x % 2 == 0){
// synchronized (this){
synchronized (SellTicket.class){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}
}else{
//x%2!=0
sellTicket();
}
x ++ ;
}
}
//如果一个方法进来就是一个同步代码块---->
// 可以将synchronized抽取到方法声明上:同步方法 (非静态)
//同步方法:它的锁对象:this 当前类对象的地址值引用
/* private synchronized void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}*/
静态的同步方法 ---静态的东西都和类直接相关 :锁对象---就是 (反射相关)类名.class--->class 包名.类名{} 字节码文件对象
private static synchronized void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
4.Lock–手动锁
JDK5以后Java提供了比syncrhonized(jvm来操作:自动释放锁)更广泛的锁定操作,
程序员可以在某个位置自己去加锁,(弊端):手动释放锁
java.util.concurrent.locks.Lock 接口 不能实例化
提供成员方法
-
void lock() :获取锁
-
void unlock():手动试图释放锁
-
ReentrantLock :子实现类(可重入的互斥锁!) 跟synchronized用于法相似
-
synchronized: 关键字—> 内置语言实现—通过jvm调用的
由jvm自动释放锁
它不仅仅可以应用在方法体中(同步代码块),也可以引用在方法上(同步方法)
Lock :是一个接口
手动加锁和手动释放锁
一般都是在方法中的语句中使用
public class SellTicket implements Runnable {
//100张票
public static int tickets = 100 ;
//声明一个锁:Lock
private Lock lock = new ReentrantLock() ;
@Override
public void run() {
//模拟一直有票
while(true){
//获取一个锁:某个线程执行到这块:必须持有锁
lock.lock();
//执行完毕,手动释放锁
//在开发中 :try...catch...finally :捕获异常 ,不会使用throws
//变形格式:try...catch...catch...
//try...finally...
try{
if(tickets >0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}finally {
//释放资源代码块
//释放锁
lock.unlock();
}
}
}
}
5.死锁
虽然syncrhonized可以解决线程安全问题:同步代码块/同步方法,但是执行效率低,可能出现死锁,两个线程或者多个线程出现互相等待的情况!
解决死锁问题方案:“生产者消费者思想” 必须保证多个多线程必须使用的同一个资源对象!
①
public class Student {
String name ;
int age ;
//是否存在学生数据标记
boolean flag ; //true有数据,false没有数据
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
②
public class SetThread implements Runnable {
private Student s ;
public SetThread(Student s){
this.s = s ;
}
//统计变量
int x = 0 ;
@Override
public void run() {
//不断产生数据
while(true){
synchronized (s){
//判断如果当生产者有数据
if(s.flag){
try {
//生产者有数据,需要等待消费者线程使用
s.wait(); //锁对象在调用wait()
//wait():当前锁对象该调用方法的时候,会立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当前在执行下面代码的之前:线程的执行资格被GetThread所在的消费者线程
if(x % 2 == 0){
s.name = "高圆圆" ;
s.age = 41 ;
}else{
s.name = "杨德财" ;
s.age = 27 ;
}
x ++ ;
//更改标记
s.flag = true ;
//通知对方线程:赶紧使用数据
s.notify(); //锁对象调用的唤醒方法
}
}
// Student s = new Student();
}
}
③
public class GetThread implements Runnable {
private Student s ;
public GetThread(Student s){
this.s = s ;
}
@Override
public void run() {
//模拟一直消费数据
while(true){
synchronized (s){
if(!s.flag){
//如果消费者没有数据了,需要等待生产线程产生数据
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(s.name+"---"+s.age);
//更改标记
s.flag = false ;//没有数据了
//通知对方线程:生产线程 :产生数据
s.notify() ;
}
}
// Student s = new Student() ;
}
}
④
* 测试类
* SetThread:生产数据
* GetThread:消费数据
* Student:学生数据
*
*
* 问题1 :出现数据 null--0 :
*
* 在生产资源类和消费者资源类中:分别new Student() :不是同一个对象
*
*
*
* 模拟一直有数据:加入循环操作while(true)
*
* 问题2:姓名和年龄不符
* 线程的执行具有随机性导致,多线程程序不安全
*
*
* 校验多线程安全问题的标准是什么?
* 1)查看是否是多线程环境
* 2)是否存在共享数据
* 3)是否有多条语句对共享数据进行操作
*
*
* 3)改进:使用同步代码块包括起来!
*
*
*
* 问题3:学生数据输出的时候,一次性打印很多
* 线程的一点点时间片:足够当前线程执行很多次!
*
* 优化:
* 不需要输出学生数据的时候, 一次性输出很多,而是分别依次输出
* "高圆圆" 41
* "杨德财" 27
* ...
* ..
*
*/
public class ThreadDemo {
public static void main(String[] args) {
Student s = new Student() ; //同一个资源类对象
//创建生产资源类对象
SetThread st = new SetThread(s) ;
//创建消费者资源类对象
GetThread gt = new GetThread(s) ;
// 创建线程类对象
Thread sThread = new Thread(st) ;
Thread gThread = new Thread(gt) ;
//启动
sThread.start();
gThread.start();
}
}
6.线程组
ThreadGroup 线程组表示一个线程的集合
Thread类中的方法:
Public final ThreadGroup getThreadGroup():获取当前所有的默认线程组
ThreadGroup
public final String getName():获取默认线程组的名称(默认就是main)
构造方法:
ThreadGroup(String name):构造一个新的名称的线程组
所有的线程默认的线程组就是main线程(用户线程)
4.线程池
1.线程池的使用:
(1)接口:ExecutorService-----跟踪一个或者多个异步任务
(2)如何实例化----->Executors.newFixedThreadPool(int)工厂方法:
Executors:工厂类
提供创建线程池对象的方法
public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorsService
方法:
Future<?> submit(Runnable task) :通过线程池提交异步任务
Future submit(Callable task):提交异步任务
Future:异步任务计算的结果!
第三种方式:线程池实现---->还是自定义一个类 实现Runnable接口
public class ThreadDemo {
public static void main(String[] args) {
/* new Thread(){
@Override
public void run() {
}
}.start();*/
//创建一个线程池:静态工厂模式
ExecutorService pool = Executors.newFixedThreadPool(2);
//提交异步任务
// Future<?> submit(Runnable task) :通过线程池提交异步任务
/* pool.submit(new MyRunnable()) ;
pool.submit(new MyRunnable()) ;*/
//Callable要比Runnable接口更好
//可以跟踪具体的异常错误(如果执行过程中,线程出现异常,可以跟踪异常信息!)
pool.submit(new MyCallable()) ;
pool.submit(new MyCallable()) ;
//使用完毕,关闭线程池--将底层产生的线程归还线程池中
pool.shutdown();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mSGfpN0D-1622122360804)(D:\JAVA 笔记\05-27\resource\02_线程池的引入.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8EjlwAyn-1622122360805)(D:\JAVA 笔记\05-27\resource\03_线程池的七大参数.png)]
2.定时器
java.util.Timer:
定时器 :有线程安排执行务执行一次,或者定期重复执行。
构造方法:
public Timer() 无参构造方法
成员方法:
public void cancel():取消定时器
public void schedule(TimerTask task,Date time) :在指定日期时间内执行这 个任务
public void schedule(TimerTask task,long delay):在指定的延迟时间后执行 task任务(时间:毫秒)
public void schedule(TimerTask task,long delay,long period) :在指定的 delay延迟时间后开始重复时间间隔period来执行task任务
- Java设计原则:
-
单一职责
-
接口分离
-
开闭原则
-
依赖注入原则(依赖于抽象类或者接口,而不依赖具体类实现)
public class TimerDemo {
public static void main(String[] args) {
//创建一个定时器
Timer timer = new Timer() ;
// public void cancel():取消定时器
//参数1:定时任务:抽象类,定义子类继承自TimerTask
// timer.schedule(new MyTask(timer),3000); :3秒后执行一次这个任务
/* public void schedule(TimerTask task,
long delay,
long period) :在指定的delay延迟时间后开始重复时间间隔period来执行task任务 */
timer.schedule(new MyTask(timer),2000,3000);
}
}
//定时任务
class MyTask extends TimerTask{ //TimerTask实现Runnable接口 ---会使用同步机制
private Timer t ;
public MyTask(Timer t){
this.t = t ;
}
@Override
public void run() {
System.out.println("bom...");
//关闭定时器
//t.cancel() ;
}
}
5.代理
代理:
让别人替自己本身完成一些事情!
举例: 过年回家买车票,让别人帮忙代买!
代理:
代理角色:帮助真实角色对他本身的功能进行增强(完成代理完成不了的事情!)
真实角色:只只专注于自己完成的事情
举例: 结婚这件事情
真实角色:you :你
代理角色:weddingCompany:婚庆公司
Java代理模式----结构型设计模式
静态代理
同一个接口
特点:代理角色和真实角色必须同一个接口
动态代理(后期框架底层使用的这个模式)
JDK动态代理:基于接口
CGLib动态代理:基于子类
-
多线程的实现方式2:
-
MyRunnable myrunnable = new MyRunnable() ; //MyRunnable implements Runnable接口
-
//线程类
-
Thread t1 = new Thread(myrunnable,"t1") ; //Thread类本身实现 Runnable接口
-
Thread t2 = new Thread(myrunnable,"t2") ;
-
Thread t3 = new Thread(myrunnable,"t3") ;
public class StaticProxyDemo {
public static void main(String[] args) {
//接口多态
//真实角色
Marry marry = new You() ;
marry.marry() ;
System.out.println("-----------------------");
//加入婚庆代理角色
You you = new You() ;
//创建婚庆公司
WeddingCompany wdc = new WeddingCompany(you) ;
wdc.marry();
}
}
//定义接口:Marry
interface Marry{
public abstract void marry() ;
}
//定义一个子实现类:真实角色
class You implements Marry{
@Override
public void marry() {
System.out.println("我很高兴,我要结婚了...");
}
}
//代理角色weddingCompany:婚庆公司 要实现Marry接口
class WeddingCompany implements Marry{
private You you ;
public WeddingCompany(You you){
this.you = you ;
}
@Override
public void marry() {
System.out.println("结婚之前,婚庆公司布置婚礼现场...");
you.marry();
System.out.println("结婚完毕,很高兴,给婚庆付尾款...");
}
}
System.out.println("-----------------------");
//加入婚庆代理角色
You you = new You() ;
//创建婚庆公司
WeddingCompany wdc = new WeddingCompany(you) ;
wdc.marry();
}
}
//定义接口:Marry
interface Marry{
public abstract void marry() ;
}
//定义一个子实现类:真实角色
class You implements Marry{
@Override
public void marry() {
System.out.println("我很高兴,我要结婚了...");
}
}
//代理角色weddingCompany:婚庆公司 要实现Marry接口
class WeddingCompany implements Marry{
private You you ;
public WeddingCompany(You you){
this.you = you ;
}
@Override
public void marry() {
System.out.println("结婚之前,婚庆公司布置婚礼现场...");
you.marry();
System.out.println("结婚完毕,很高兴,给婚庆付尾款...");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-01gVyEjj-1622122360807)(D:\JAVA 笔记\05-27\resource\01_代理真实用途.png)]