今日学习内容总结
- 线程
- 等待唤醒机制
线程
多线程原理
-
多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈
多线程原理
Thread类
常用方法:
- public String getName() :获取当前线程名称。
- public static Thread currentThread() :返回对当前正在执行的线程对象的引用
- Thread.currentThread.getName();获取当前正在执行的线程的名称
- public Thread(String name) :分配一个指定名字的新的线程对象
- public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字
- public void setName(String name) : 为线程对象设置名字.
- public static void sleep(long millis):是当前正在执行的线程以指定的毫秒数暂停
创建线程的第二种方式
实现步骤:
- 1、创建一个Runable接口的实现类
- 2、在实现类中重写Runnable接口的run方法,设置线程任务
- 3、创建一个Runnable接口的实现类对象
- 4、创建Thread类对象,构造方法中传递Runnable接口的实现类对象
- 5、调用Thread类中的start方法,开启新线程执行run方法
public class MyThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
public class Demo01main {
public static void main(String[] args) {
MyThread myThread=new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
实现Runnable接口创建多线程程序的好处:
1.避免了单继承的局限性
一个类只能继承一个类(一个人只能有一个亲爹),类继承了Thread类就不能继承其他的类
实现了Runnable接口,还可以继承其他的类,实现其他的接口
2.增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
实现类中,重写了run方法:用来设置线程任务
创建Thread类对象,调用start方法:用来开启新线程
匿名内部类方式实现线程的创建
方式一:
new Thread(){
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}.start();
方式二:
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}).start();
线程安全
多个线程访问同一个共享的元素时会发成线程安全问题
线程同步
- 1、同步代码块
- 2、同步方法
- 3、Lock锁机制
同步代码块
synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
1.通过代码块中的锁对象,可以使用任意的对象
2.但是必须保证多个线程使用的锁对象是同一个
3.锁对象作用:
把同步代码块锁住,只让一个线程在同步代码块中执行
同步方法
同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
格式:修饰符 synchronized void 类名称(){}
Lock锁机制
方法:
- public void lock() :加同步锁。
- public void unlock() :释放同步锁
ReentrantLock是Lock的实现类
使用步骤:
- 1、在成员位置创建一个ReentrantLock对象
- 2、在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
- 3、在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
线程状态
线程状态概述
- NEW 至今尚未启动的线程处于这种状态。
- RUNNABLE 正在 Java 虚拟机中执行的线程处于这种状态。
- BLOCKED 受阻塞并等待某个监视器锁的线程处于这种状态。
- WAITING 无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
- TIMED_WAITING 等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
-
TERMINATED 已退出的线程处于这种状态。
线程状态
public static void main(String[] args) {
Object obj = new Object();
new Thread() {
@Override
public void run() {
synchronized (obj) {
System.out.println("告诉老板要买的东西");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("已得到要买的东西,结账!");
}
}
}.start();
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {
System.out.println("5s后老板将物品拿给顾客...");
obj.notify();
}
}
}.start();
}
等待唤醒机制
等待唤醒机制
等待唤醒机制就是用于解决线程间通信的问题的,使用到的3个方法的含义如下:
- wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
- notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先入座。
- notifyAll:则释放所通知对象的 wait set 上的全部线程。
调用wait和notify方法需要注意的细节
- wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
- wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
- wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。
网友评论