多线程
1. 并发与并行
- 并发:指两个或多个事件在同一个时间段内发生。
- 并行:指两个或多个事件在同一时刻发生(同时发生)。
注意:单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。同理,线程也是一样的,从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为线程调度。
2. 线程与进程
-
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
-
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
线程调度:
- 分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
- 抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
3. 创建多线程
- 定义Thread的子类,并重写该类的run()方法,改run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体
- 创建Thread子类的实例
- 调用线程对象的start()方法来启动该线程
线程类
1. 多线程原理
2. Thread类
构造方法:
public Thread()
:分配一个新的线程对象public Thread(String name)
:分配一个指定名字的新的线程对象public Thread(Runnable target)
:分配一个带有指定目标新的线程对象public Thread(Runnable target,String name)
:分配一个带有指定目标新的线程对象并指定名字
常用方法:
public String getName()
:获取当前线程名称public void start()
:导致此线程开始执行;JVM调用此线程的run方法public void run()
:此线程要执行的任务在此处定义代码public static void sleep(long millis)
:使当前正在执行的线程以指定的毫秒数暂时停止执行public static Thread currentThread()
:返回对当前正在执行的线程对象的引用】
3. 第二种创建线程的方式
方式:实现Runnable接口
实现步骤:
1. 创建一个Runnable接口的实现类
2. 在实现类中重写Runnable接口的run方法,设置线程任务
3. 创建一个Runnable接口的实现类对象
4. 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
5. 调用Thread类中的start方法,开启新的线程执行run方法
4. Thread与Runnable的区别
实现Runnable接口的优势:
- 避免了单继承的局限性:实现了Runnable接口,还能实现其它的接口,也能继承一个其它的类
- 增加了程序的健壮性,实现了解耦操作
5. 匿名内部类方式实现线程的创建
public class InnerClassThread {
public static void main(String[] args) {
//父类
new Thread() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "-->A");
}
}
}.start();
//Runnable接口
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "-->B");
}
}
};
new Thread(r).start();
//简化Runnable接口
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "-->C");
}
}
}).start();
}
}
线程安全
1. 线程安全
在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
2. 线程同步
解决多线程并发访问一个资源的安全性问题,Java中提供了同步机制(synchronized)来解决。
某个线程修改共享资源的时候,其他线程不能修改该资源,等待修改完毕同步之后,才能去抢夺CPU
资源,完成对应的操作,保证了数据的同步性,解决了线程不安全的现象。
3. 同步代码块
synchronized
关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:
synchronized (同步锁) {
需要同步操作的代码
}
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着
(BLOCKED)。
4. 同步方法
使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外
等着。
格式:
public synchronized void method(){
可能会产生线程安全问题的代码
}
5. LOCK锁
public void lock()
:加同步锁。public void unlock
:释放同步锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class RunnableImpl implements Runnable {
private int ticket = 100;
Lock l = new ReentrantLock();
@Override
public void run() {
while (ticket > -1) {
l.lock();
if (ticket > 0) {
try {
Thread.sleep(1);
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
}
}
}