线程
程序: 为完成特定的功能,使用计算机语言编写的一系列的指令集合,即静态代码
进程: 运行中的程序,被加载到内存中,是操作系统分配资源的最小单位
线程: 是进程中的最小的执行单元(任务),是操作系统进行任务调度的最小单位,隶属于进程
进程和线程的关系
一个线程只能属于一个进程,线程不能脱离进程
一个进程中至少有一个线程(主线程) java中main方法就是用来启动主线程
在主线程中可以创建并启动其它的线程;
一个进程内的所有线程共享该进程的内存资源
创建线程的两种方式
继承Thread类的方式
在Java中要实现线程,最简单的方式就是扩展Thread类,重写其中的run方法,方法原型如下:
public class MyThread extends Thread {
public void run() {
}
}
调用方式:
MyThread thread = new MyThread();
thread.start();
实现Runnable接口的方式
public class PrintNum implements Runnable{
public void run() {
}
}
调用方式:
PrintNum printNum = new PrintNum(); //创建线程
Thread thread = new Thread(printNum);
thread.start();
继承方式和实现方式的区别
继承Thread: 线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。
实现Runnable的好处
-
避免了单继承的局限性
-
多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
Thread类中常用方法
run() //线程任务执行的方法
start() //起动线程
Thread(target) //创建线程,
(target,name)//创建线程,并指定任务,定义线程名称
currentThread(); //获取到当前所在线程
getName()// 获得线程的名称
setName() // 重写线程的名称
setPriority(10); // 设置优先级
getPriority(); //获得优先级
sleep(1000);// 线程休眠指定的时间(毫秒单位)
yield() //线程让步
join() // 等待该线程结束
Java的调度方法
-
同优先级线程组成先进先出队列,使用时间片策略
-
对高优先级,使用优先调度的抢占式策略
线程状态
线程在它的生命周期中会处于不同的状态:
线程的状态:
新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
**就绪:**处于新建状态的线程被start()后,将进入线程队列等待CPU时 间片,此时它已具备了运行的条件,只是没分配到CPU资源
**运行:**当就绪的线程被调度并获得CPU资源时,便进入运行状态,run ()方法定义了线程的操作和功能
**阻塞:**在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态
**死亡:**线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
守护线程
Java中的线程分为两类:用户线程和守护线程
任何一个守护线程都是整个JVM中所有非守护线程的保姆,只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
守护线程的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC(垃圾回收器),它就是一个很称职的守护者。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了.
设置守护线程: setDaemon(boolean on)
注意:设置线程为守护线程必须在启动线程之前,否则会跑出一个IllegalThreadStateException异常。
多线程
一个应用程序内部,可以同时执行多个任务
优点:
提高程序处理能力,响应速度提高.
提高cpu利用率, 压榨硬件的价值
提升程序结构
缺点:
对内存,cpu要求提高了, 提升硬件性能改善
多个线程对同一个共享资源进行访问会相互影响
线程同步
并发与并行
● 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
● 并发:在一个时间段内一次执行操作.例如卖票,抢购,秒杀看似同时进行,实际是一个一个执行.
确保一个时间点只有一个线程访问共享资源。可以给共享资源加一把锁,哪个线程获取了这把锁,才有权利访问该共享资源。
synchronized
使用synchronized(同步锁)关键字同步方法或代码块
synchronized修饰非静态的方法时,锁对象是this
synchronized修饰静态方法时,锁对象是当前 类的Class对象
public (static) synchronized void ptint(){
if(num>0){ System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}
}
synchronized修饰的代码块称为:同步代码块
synchronized(唯一的一个对象,可以是任何对象,此对象用来记录有没有线程进入同步代码块)
synchronized (obj){
// 需要被同步的代码;
}
同步对象/锁对象-- 用来记录有没有线程进入到同步代码块, 要求是唯一的,多个线程对应的是同一对象
Lock
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁.
Lock lock = new ReentrantLock();/创建对象
lock.lock();//获取锁
lock.unlock();//释放锁
ReentrantLock 和 synchronized 区别
synchronized是关键字,实现是依靠底层编译后的指令来控制的.
synchronized可以修饰代码块和方法
synchronized是隐式锁,自动添加锁,同步代码块执行完毕或者出现异常,锁会自动释放
ReentrantLock是java.util.concurrent.locks包下的一个类,是依靠java代码实现控制
ReentrantLock只能修饰代码块
ReentrantLock是手动添加,手动释放
线程死锁
死锁:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步 资源,就形成了线程的死锁.出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续.
线程通信
线程通讯指的是多个线程通过相互牵制,相互调度,即线程间的相互作用。
涉及三个方法:
.wait一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
.notify一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
.notifyAll一旦执行此方法,就会唤醒所有被wait的线程。
注意:
.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方 法中。