一、笔记
线程
并发(高并发) ==>在同一个时间段,来执行两个或者是多个操作 (交替执行) (一人吃两个馒头)
并行 ==> 在同一个时刻执行,两个或者是多个操作 (两个人吃两个馒头)
进程: 一个正在运行程序,在内存里开辟过空间 比如查看电脑的进程: ctrl+alt+delete
线程:表示每一条执行的路径
进程与线程之间的关系:
一个进程里可以存在多个线程, 而一个线程只能在一个进程里
线程分配情况 :
1. 叫做 按照时间均匀的分配 (两个线程 交替执行,以时间为标准) (不存在)
2.抢占式分配: 任意一个线程, 谁能够抢到cpu的资源谁执行(java)
单纯的执行main 方法:属于单线程,从上到下执行
java 里支持多线程: 如何来实现多线程:
第一种方式:
继承线程类 类:Thread Java 虚拟机允许应用程序并发地运行多个执行线程
实现多线程的步骤:
`1. 创建一个Thread 的子类
2. 重写其run() run方法也就是子线程要执行的操作
3 . 实例化这个子类
4. 调用 start() 开启线程`
==> run() 如果直接调用run 是在主线程里执行,没有cpu新开辟一条执行的路径
==>start() 只有调用这个方法才会去开辟一条心的路径
注意点:
一个线程只能够开启一次,重复开启会产生错误: java.lang.IllegalThreadStateException
第二种方式:
实现这个接口 Runnable
实现的步骤:
1.定义一个类来实现这个接口
2. 实现run()方法
3.实例化和这个实现类
4.实例化一个Thread,传递参数是 是这个实现类
5.调用start方法
好处:
1.解决java单继承的问题
2. 可扩展性增强, 实现申明与调用的分离 降低了耦合度
3.继承Thread 还是实现了这个接口
坏处:实现多线程效率:对于整个系统来说,效率是提高,但是对于具体的某一个线程,效率就是降低
使用类名内部来实现多线程:
第一种方式:
Thread thread = new Thread(new Runnable() {})
第二种方式 :
Thread thread = new Thread(runnable);
Runnable runnable = new Runnable() {}
线程中常规的一些方法:
public static Thread currentThread() 返回对当前正在执行的线程对象的引用(也就是当前线程)
----------------------------------------------------------------------------------------------------------------------------
getName() 获取线程的名称 线程名字的默认规则是 Thread+i(i 是从0开启)
---------------------------------------------------------------------------------------------------------------------------
public final void setName(String name) 设置线程的名称 (一定要在开启线程之前进行设置)
==》也可以通过构造的方式来设置线程的名称: public Thread(Runnable target,String name)
----------------------------------------------------------------------------------------------------------------------------
public static void sleep(long millis) 表示让当前线程睡一会,别的线程可以执行了
----------------------------------------------------------------------------------------------------------------------------
public final void setPriority(int newPriority) 设置线程的优先级 最小是1 默认是5 最大是10
注意: 设置优先级不一定是最先执行,只是优先执行的机遇更大一点
----------------------------------------------------------------------------------------------------------------------------
public final void join() 如果有两个线程: A线程 调用了join方法, A线程执行完成之后,B线程才能执行
注意点
1. 这个方法必须在线程开启之后进行调用
2. 这个方法底层还是调用了wait()方法
----------------------------------------------------------------------------------------------------------------------------
public static void yield() 礼让 : 如果有A线程 B线程, 这如果A线程调用了yield() 并不一定b线程就会执行
----------------------------------------------------------------------------------------------------------------------------
public final void setDaemon(boolean on) 守护线程 如果A,B 线程 A是被守护线程 B守护线程 如果A终止了,B线程也会终止
----------------------------------------------------------------------------------------------------------------------------
public final boolean isDaemon() 判断其是否是守护线程
抢电影票
三种方案都是为了解决问题: 解决多线程中共享数据的问题
解决的第一种方案:
同步代码块
语法: synchronized (){}
分析:
同步的关键字 synchronized
() 跟着是对象,这个对象可以是任意的对象
{} 会出现共享数据问题的代码
注意点:
1.对象可以是任意的对象
2.三个对象必须是这个锁对象
3.锁对象,保证只有一个线程进入
第二种解决方案:
也就是同步方法:
1.也就是把可能出现数据问题的代码抽取成一个方法
2.给方法加上锁的关键字
3..锁的对象是 this
注意:
static 修饰的同步方法,使用的锁不是this,而是字节码文件,即类名.class。
所以,要使得是同步,则同步代码块的锁对象必须是字节码文件,如:synchronized (ticket.class) 。
第三种解决方案:
Lock是一个接口
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
此实现允许更灵活的结构实现类:(常规的两个方法: )
public void lock() 获取锁
public void unlock() 释放锁
使用步骤:
1.实例化这个对象
2.在同步可以出现数据异常的头部调用其lock()
3.在同步可以出现数据异常的 尾部加上 unlock() (不是特别常用)
二、总结
第二十一天的学习,今天学习了线程,虽然之前在学校有接触过但都是很浅的,没有深入去了解,今天终于弄明白了很多之前不懂的,重点要掌握的是同步代码块的使用。