线程的学习笔记
线程和进程的区别
- 线程是资源调用的最低单位
- 进程是cpu 调用的最低单位
- 一个进程至少一个的线程
线程的两种创建方式以及优缺点:
继承 *
Thread
*的方法class Test extends Thread{
public void run(){
// 重写run方法
}
}
public static void main(String[] arg){
Test t=new Test();
t.start();
}
实现
Runnble
接口class Test implements Runnable{
// 重写run方法
public void run(){}
public static void main(String[] arg){Test t=new Test();
Thread t=new Thread(t).start():
}
}
使用Callable和Future创建线程
- 使用线程池的方式创建线程
两种方式的优缺点
继承的优点
- 可以直接调用Thread 的父类方法
- 创建简单 使用简单
- 如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程
缺点
- java 是单继承的机制,继承Thread 的话不能继承其他
实现接口的优点
- 只是实现了Runable接口,还可以继承其他的类
- 可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想
缺点
- 编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法
注:java中推荐使用接口方式实现线程
线程的五种状态
创建 new :当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
可运行(就绪) Runnable :当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行 Running :当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞 Blocked :处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
- 等待式阻塞 :运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
- 同步式阻塞 :线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态
- 其他阻塞 :通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态
- 终止 Dead:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程的同步方式
线程安全原因:
在
java
中在使用多线程加快运行效率的同时因为因为cpu的使用机制问题在多个线程访问同一个资源的同时会可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错所以就产生线程安全问题解决方式
使用同步代码块
Object o=new Object();
synchronized (o){// 需要同步的线程代码
}
}
使用同步方法
public synchronized void Test2(){
}
Lock 方式
public void Test2(){
Lock lock=new ReentrantLock();
lock.lock();//开启同步
lock.unlock();//关闭同步
}
线程的生命周期
线程之间的通信
什么是线程之间的通信:
- 多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。就是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。
wait()方法
- 在线程中调用的notfiy() 和notfiyAll()之前一直保持沉睡状态导致当前线程停止
- 调用wait()方法会释放锁对象然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。
- 要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。
notfiy()
- 如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个)
- notify()方法应该是被拥有对象的锁的线程所调用
wait/notfiy机制
用创建者和消费者来简单的演示下线程的通信
1.创建包子对象(也是个锁对象)
public class baozi {
private int num;
public baozi(int num) {
this.num = num;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public void add() {
num++;
}
public void remove() {
num--;
}
}
2.创建消费者对象
public class customer implements Runnable {
baozi baozi;
public customer(baozi baozi) {
this.baozi = baozi;
}
@Override
public void run() {
while (true) {
synchronized (baozi) {
if (baozi.getNum() == 0) {
try {
baozi.wait();//当包子为零的时候停止消费包子
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
baozi.remove();//当包子不为零的时候就开始消费包子
System.out.println("包子还剩" + baozi.getSize() + "个");
baozi.notify();//当执行完后 唤醒其他线程生产包子
}
}
}
}
}
3.创建消费者对象
public class customer implements Runnable {
baozi baozi;
public customer(baozi baozi) {
this.baozi = baozi;
}
@Override
public void run() {
while (true) {
synchronized (baozi) {
if (baozi.getNum() == 0) {
try {
baozi.wait();//当包子为零的时候停止消费包子
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
baozi.remove();//当包子不为零的时候就开始消费包子
System.out.println("包子还剩" + baozi.getSize() + "个");
baozi.notify();//当执行完后 唤醒其他线程生产包子
}
}
}
}
}
4.创建测试类
package com.zerolg.ThreadDemo.Test;
public class Test {
public static void main(String[] args) {
baozi baozi = new baozi(10);
customer customer = new customer(baozi);
generate generate = new generate(baozi);
Thread thread = new Thread(customer);
thread.start();
Thread thread1 = new Thread(generate);
thread1.start();
}
}
效果:
如上所示,案例表明生产者消费者之间就是运用了wait()和notify()这两个方法,通过通信的沉睡与唤醒机制来完成两个不同线程操作统一数据之间的通信
还没有评论,来说两句吧...