概念
多线程是指程序当中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个线程创建多个并行执行的线程来完成各自的任务。
何时需要多线程
程序需要同时执行两个或多个任务。
程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
需要一些后台运行的程序时。
多线程的优点
提高程序的响应
提高CPU的利用率
改善程序的结构,将复杂的任务分为多个线程,独立运行。
多线程的缺点
线程也是程序,所以线程也是需要占用内存,线程越多占用内存也是越多的。
多线程是需要协调和管理,所以这也是需要CPU时间跟踪线程的。
线程之间对共享资源的访问会互相影响,必须解决竞用共享资源的问题。
线程同步
先看下面的一段代码:
在这样的输出绝对是有问题的。
所以在处理多线程的问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。这时候我们就需要线程的同步。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程再使用。
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题,为了保证数据在方法中被访问的正确性,在访问时加入锁机制synchronized,在当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可。
这里也存在下面的问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起。
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延迟,引起性能问题。
- 如果一个优先级高的线程等待的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题。
在Java中实现同步
使用synchronized(同步监视器)这个关键字来同步方法或代码块
synchronized(同步监视器){
}
synchionized还可以放在方法声明中,表示整个方法,为同步方法。
public synchionized void show(Sting name){
//需要同步的代码
}
同步代码块,执行完毕后,同步对象(锁)就会自动释放
当然在synchronized的时候,也可以直接传进去关键字this,表示当前的。只要表示出唯一性就可以了。
同步方法:
public class TicketThread implements Runnable {
static int num = 10;
@Override
public void run() {
while (true) {
if (num > 0) {
print();
} else {
break;
}
}
}
//在修饰方法的时候,同步对象(锁),默认的是this,一旦创建多个线程对象就崔出现一些问题(只针对继承Thread类的方式)
public synchronized void print() {
if (num > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售出票:" + num);
num--;
}
}
}
当然上面写的都是实现Runnable接口来实现的。
当然还是可以继承Thread类来实现的,在用thread来写的时候,一定要注意好只能由1个来实现的,该加static的时候就要加static来修饰,使得虽然创建了多个,但实际只能还是一个存在的。
上面代码中有提到的同步监视器是什么呢?
同步监视器可以是任何对象,但必须保证唯一,保证多个线程获得的是同一个对象(锁)
同步监视器的执行过程:
- 第一个线程访问,就锁定同步监视器,执行其中的代码
- 第二个线程访问,发现同步监视器被锁定的话,就无发访问
- 第一个线程结束访问,就解锁了同步监视器。
- 第二个线程访问,发现同步监视器没有锁,就可能正常访问,并加锁。
锁机制
上面提到的synchronized就是一个隐式的加锁,释放锁。
在这提到到的Lock是可以显示的加锁,释放锁的,但只能锁定代码块。
Lock:
是从jdk 1.5开始的,Java提供了更强大的线程同步机制-通过显示的定义同步锁对象来实现同步。同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独立访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentranlLock,可以显示加锁,释放锁。
对我们上面写的TicketThread类的代码做做修改:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class