程序:序指的是为完成某种任务,用某种语言编写的一组指令的集合。
进程:进程指的是程序的一次执行过程,或者是正在运行的一个程序,也就是说当一个程序被
运行,就开启了一个进程,
线程:进程可以进一步细化为线程,是一个程序内部执行的一条路径,也就是说一个进程可以
有多个线程,一个线程就是一个指令流,cpu 调度的最小单位,由 cpu 一条一条执行指令,
每个线程拥有独立的线程栈和程序计数器。如果一个进程同时执行多个线程,那么就是多线
程。例如我们打开某个软件然后同时使用该软件里的两个功能,这也叫多线程。
并行:多个 cpu 同时执行多个任务。
并发:一个 cpu 同时执行多个任务。
多线程的创建:
1.继承Tread类:class类extendsThread{
public void run(){
//Thread类提供了一个静态方法名字叫做getname()用于获取线程名
线程要执行的任务必须放在该方法里面
}
}
类 对象名 =new 类
对象名.start//用于开启一个线程
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Dome1 { public static void main(String[] args) { MyThread1 my1=new MyThread1("THIS1"); MyThread1 my2=new MyThread1("THIS2"); my1.start(); my2.start(); } } class MyThread1 extends Thread{ public static Lock l=new ReentrantLock(); public MyThread1(){ } public MyThread1(String name){ super(name); } public void run(){ l.lock(); for (int i = 0; i < 10; i++) { System.out.println(getName()+"-------"+i); } l.unlock(); } }
run方法和start方法区别start方法只能调用一次,run方法里面存放的是线程任务
2.实现Runable接口
class 线程类 implements Runable{
public void run(){
线程任务
}
}
线程类 线程对象 =new 线程类
Thread thread=new thread()
thread.start()
继承 Thread 类和实现 Runable 接口都可以去创建线程,这二者实际上并没有可比性, 它们只是接口和类的区别,同时也是继承关系。
我们通过继承 Thread 类去创建线程的时候需要注意的是 Thread 的实现类里面的 run 方 法只是定义了线程需要执行的任务,如果直接调用 run 方法就相当于在主线程 main 中调用 一个类的普通的方法,并不会重新开启一个线程。
public class Titck { public static void main(String[] args) { WindowSal sal = new WindowSal(); Thread thread1 = new Thread(sal,"窗口一"); Thread thread2 = new Thread(sal,"窗口二"); Thread thread3 = new Thread(sal,"窗口三"); thread1.start(); thread2.start(); thread3.start(); } } class WindowSal implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ sal(); } } private synchronized void sal(){ if (ticket > 0){ System.out.println(Thread.currentThread().getName()+"正在售卖第" +(1000-ticket+1)+"张票"+"还剩下:"+(ticket-1)+"张票。"); ticket--; }else { Thread.currentThread().stop(); } } }
3.实现Callable接口
Callable 类型线程是通过 FeatureTask 类来实现的,而 FeatureTask 是 Feature 接口的一个实现类,我们还可以通过 FeatureTask 中 get 方法去获 取 call 方法中的返回值。
FeatureTask 类它实现了 RunnableFuture,然后 RunableFuture 又实现了 Runnable, 和 Future,因此 FeatureTask 也能处理 Runable 类型任务。
Callable 和 Thread 类、Runable 接口一样都是创建线程的方式,其中 Thread 类和 Runable 接口通过 run 方法实现线程的任务,Callable 接口通过 callable 方法实现线程的任务,并且 callable 方法是有返回值的
4.线程池
线程的调度:cpu会给线程分配时间片,一旦得到了时间片,他就可以执行,
哪个线程抢到时间碎片,那么该线程就执行,没有抢到时间碎片的线程机会处于一个等待状态
线程的生命周期:
⚫ 新建:当一个 Thread 类或其子类被创建声明后,新生的线程对象就处于新建状态。
⚫ 就绪:当处于新建状态的线程对象调用 start 方法以后,它就进入线程队列等待 cpu 时间片,此时线程对象已经具备了运行条件,只是没有得到 cpu 资源。
⚫ 运行:处于就绪状态的线程对象获得 cpu 资源以后就可以进入运行状态,run 方法 定义了线程的任务。
⚫ 阻塞:在某些特殊情况下,被人为挂起或执行输入输出操作的时候,线程对象让出 自己的 cpu 并终止任务进入阻塞状态。
⚫ 死亡:线程对象完成了它的线程任务或者被强制退出、发生异常等,线程对象就进 入死亡状态。
Thread的几个常用方法:
1.运行状态--》阻塞---》就绪
sleep()可以让当前线程休眠,知道休眠时间结束才让当前新城重新去竞争cpu时间片
join()插队
wait() 等待
yield()可以让一个线程从运行状态回归到就绪状态
yield()可以让当前线程释放时间碎片,进入就绪状态重新等待时间碎片
线程安全:
1.使用线程安全
2.synchronized:在java中就是一个对象
假如一个对象最先拿到锁,拿到锁之后他就会进入代码运行,并且代码锁住,别的对象不可调用
synchronized(对象){
代码块
}//对象:对象.class
同步代码块就是将需要的代码装载在代码块里面
同步方法就是在方法上面加上一个synchronized关键字
synchronized 的作用即线程的
1.原子性2.有序型3.可见性
死锁:锁不释放并开始抢占,死锁原因(加锁顺序不当)
解决死锁:
1.改变加锁顺序
2.使用lock锁(手动锁:自己加锁自己释放锁)
3.lock:手动锁 :lock()上锁 unlock()解锁trylock()
trylock():和lock()方法一样也是用于加锁,只是他有返回值;
若枷锁成功返回true否则返回flase
需要注意的是若是加锁失败则不能去unlock解锁
trylock(long time,timeUnit unit):trylock类似,只是该方法加入了时间,
即规定时间内加锁成功就返回ture,否则返回flase
该方法会抛出一个异常即Int
lock和synchronized区分:
1.synchronized是关键字,lock是接口
2.synchronized的加锁和释放锁是自动的lock则相反
3.lock可以监听到锁的状态
4.synchronized会造成死锁lock不会
利用wat(),join(),sleep()等方法去实现线程之间的一个交互
wati()和notify()配合使用可以完成线程间的一个通讯
T1和T2让他们交替打印数字1-20
Volatile:Volatile 不能保证代码的原子性,但是可以保证可见性和有序性(禁止代码重 排)。还需要注意的是 Volatile 只能修饰变量
notify()----用于唤醒另外一个被wait的线程
wati()----用于将当前线程进入阻塞状态,并释放锁
//wait()要抛出一个异常
wait和notify必须放在synchronized同步方法或者代码块里面
通过锁对象去调用wait和notify方法
notifyall()和notify
1.notify用于唤醒一个线程
2.notify用于唤醒所有个线程
wait和sleep的区别
sleep不会释放锁wait会释放锁
sleep自动唤醒,wait需要配合notify手动唤醒
callable:
FutureTask可以用于接受callable对象
FutureTask还提供了一个方法get----用于拿取返回值
由于FutureTask实现了Runnable接口,对此它也是Runnable的一个实例对象
Callable 接口通过 callable 方法实现线程的任务,并且 callable 方法是有返回值的。