进程Process
进程就是操作系统中执行的程序,一个程序就是一个执行的进程实体
每个运行中的进程,都有属于它的内存空间,各个进程互不影响
线程Thread
线程是一个进程中的执行单元,一个进程中可以有多个线程
多个线程,可以访问同一个进程中的资源
每个线程都有一个独立的栈空间,这些线程所在的占空间位于同一个进程空间中
多线程
如果一个进程中,同时在执行多个线程,就成为多线程
多线程你可以提高程序执行的效率,入多个窗口买票,可以加快卖票的效率
每个执行的Java程序,都是多线程执行,main方法称为主线程,还有gc线程(垃圾回收机制)在同时运行
如: 有一个工厂,工厂中有很多车间,每个车间有很多流水线
工厂就是内存,车间就是进程,每个流水线都是一个进程中的多个线程.
并行和并发
并行
每个进程同时各自执行,称为并行
并发
多个线程同时执行,称为并发
同步与异步
同步
所有的任务排队执行,成功为同步执行.
异步
在执行任务A的同时执行任务B称为异步执行
Java中的线程Thread类
java中线程以对象的形式存在
Thread表示线程类
获取线程对象
- 获取当前正在运行的的线程对象
//获取当前正在运行的线程对象
Thread ct = Thread.currentThread();
- 创建一个线程对象
构造方法
常用构造方法 | 说明 | |
---|---|---|
Thread() | 创建一个默认的线程对象 | |
Thread(String name) | 创建一个指定名称的线程对象 | |
Thread(Runnable target) | 将一个Runnable对象包装为线程对象 | |
Thread(Runnable target,String name) | 将一个Runnable对象包装为线程对象并指定名称 |
常用方法
方法 | 作用 |
---|---|
getId() | 获取线程的ID主线默认为1 |
getName() | 获取线程名,主线程名为main,也可以用setName设置名字 |
getPriority() | 得到该线程的优先级,默认线程优先级为5 |
getState() | 判断该线程是否属于守护线程,也可通过setDaemon()更改 |
setName(String name) | 设置线程名 |
setPriority(int priority) | 设置该线程的优先级,设置的范围为: 1-10 ,越大越优先 |
isDaemon() | 判断该线程是否属于守护线程 |
setDaemon(boolean on) | 设置该线程是否是守护线程 |
start() | 让线程进入就绪状态 |
run() | 线程获得执行权执行的方法 |
Thread.sleep(long m) | 设置当前线程休眠m毫秒 |
Thread.currentThread(); | 获取当前执行的线程对象 |
Thread.yield() | 线程让步 |
实现多线程
方式一: 定义一个类继承Thread类
-
创建一个类继承Thread
-
重写Thread中的run()方法.
-
创建自定义的线程子类,调用start()方法
自定义线程类Thread的子类
public class ThreadA extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(super.getName()+" : " + i);
}
}
}
方式二:实现Runnable接口
由于Java是单继承,如果某个类已经使用了extes关键字,可以使用Runnable接口实现多线程
-
自定义一个类,实现Runnable接口
-
重写run方法
-
创建Runnable接口的实现类对象
-
使用Thread参数为Runnabel的构造方法,上一步的对象作为参数,包装为Thread对象
-
调用start()方法
自定义Runnable接口的实现类
public class ThreadB implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" : " + i);
}
}
}
main
Thread myThread = new Thread(new ThreadB(),"My线程B");
Thread myThread2 = new ThreadA();
myThread2.setName("我的线程A");
myThread2.start();
myThread.start();
方式三:使用匿名内部类
如果不想创建一个Runnable的实现类,就可以使用匿名内部类充当Runnable接口的实现类
//声明内部类,并且调用start()方法
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" : "+i);
}
}
},"内部类线程").start();
线程的生命周期
线程的初始化到终止的整个过程,称为现成的生命周期
新生状态
当线程对象被创建后,进入新生状态. new了一个线程对象时
就绪状态
当某个线程对象调用了start()方法后,就进入了就绪状态
在这个状态下,线程对象不会做任何事情,只在等他的CPU调度.
运行状态
当某个线程对象得到CPU时间片(CPU执行这个线程的机会所给的时间),则进入运行状态,开始执行run()方法
不会等待run()方法执行完毕.只会在指定的时间内尽可能地执行这个run()方法,只要调用玩run()方法后就会在进入就绪状态
阻塞状态
如果某个线程遇到了sleep()方法或者wait()方法就会进入阻塞状态
sleep()发给发会在指定时间后让线程重新就绪
wati()方法只有在被调用notify()或notifyAll()方法唤醒后才能重新就绪.
终止状态
当某个现成的run()方法中的的所有内容都执行完,就会进入终止状态,意味着该线程的使命已经完成.
守护线程
如果将一个线程setDeamon(true),表示该线程为守护线程.
守护线程会随着其他非守护线程终止而终止
多线程访问同一个资源
如银行存款100,同一时刻在手机和ATM一起取出,如果用多线程模拟,可能会出现两个线程都取出100的情况,需避免这种形况发生
解决方案
让线程同步(排队)执行即可.某个线程执行run()时,让其他线程等带run()方法的内容执行完毕.
synchronized关键字
这个关键字可以先修饰方法或者代码块
修饰方法
写在方法的返回值之前,这是该方法就成为同步方法
public synchronized void fun() {
}
这时就会排队执行该方法
修饰代码块
当某个对象会被多个线程同时操作时,可以使用
synchronized (被同时操作的对象){ //排队执行的代码
}
这样在涉及到该对象操作时,就会排队执行
写在一个独立的 { } 前,称为一个同步的代码块
代码块在类创建对象时执行
synchronized (要同步的对象或this){
//排队执行的代码
}
原理
每个对象默认都有一把"锁",当某个线程运行到被synchronized修饰的方法时,该对象就会拥有这把锁,再拥有锁过程中,其他线程不能同时访问该方法,只有等待其结束后,才会释放这把锁
使用synchronized修饰后的锁称为"悲观锁".
方法被synchronized修饰后称为同步方法,就会让原本多线程变成了单线程(异步变为同步).
死锁的解决方式
方式一:
让两个线程获取资源的顺序一致,
方式二:
让两个线程在获取资源A和资源B之前再获取第三个资源,对第三个资源使用
synchronized进行同步,这样某个线程在获取第三个资源后,将后续内容执行完毕,其他线程才能执行
就是在死锁外再增加一层锁,在多线程情况中,当有线程拥有该锁时,其他线程不能访问该锁内的两个资源.