并发运行:线程之间互相抢占执行权的状态。
- 线程中并发指一个时间段中多个线程都处于已启动但没有运行结束的状态。
- 多个线程之间默认并发运行,这种运行方式往往会出现交叉的情况。
刚开始我很不理解为什么要用串行?多线程的作用不就是把程序并行执行加快运行速度吗?
现在又要将程序串行起来,让程序更加有秩序吗?
串行运行:使原本并发运行的多个线程实现串行运行,即多线程间同步执行,需要通过对象锁机制来实现,synchronized就是一个利用锁实现线程同步的关键字。
下看下列代码如何实现多线程共享?
package keeper;
import java.util.Date;
public class Test {
public static void main(String[] args) {
Object object =new Object();
new countThread("******",object).start();
new countThread("######",object).start();
}
}
class countThread extends Thread{
Object object;
countThread(String name,Object object) {
super(name);
this.object =object;
}
@Override
public void run () {
synchronized (object) {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "," + new Date()+"@@@@@"+i);
}
}
}
}
通过synchronized”锁住“object对象实现的。如果谁先拿到执行权,谁就可以开启这把锁,进入程序执行知道该线程结束后,才开始执行下一个就绪线程。而且值得注意的是,synchronized不会决定线程的执行权,只会锁住被修饰的代码。
如果在run方法内第一行写上
System.out.println(1111);
那么也会输出,因为只锁住被修饰的代码,该代码没有别synchronized修饰。
多线程同步原理:
为什么通过synchronized就能实现多线程间串行运行呢?
- 被synchronized括着的部分就是线程执行临界区,每次仅能有一个线程执行该临界区中的代码:当多个线程中的某个线程先拿到对象锁, 则该线程执行临界区内的代码,其他线程只能在临界区外部等待,当此线程执行完临界区中的代码后,在临界区外部等待的其他线程开始再次竞争以获取对象锁,进而执行临界区中的代码,但只能有一条线程“胜利”。
- 临界区中的代码具有互斥性、唯一性和排它性:一个线程只有执行完临界区中的代码另一个线程才能执行。
下面分析一段代码:
思考:为什么这行代码System.out.println(time);60秒左右才会执行?
import java.text.*;
import java.util.Date;
public class Test {
public static void main(String[] args) {
Object lockObj = new Object();
new DisplayThread(lockObj).start();
}
}
class DisplayThread extends Thread {
Object lockObj;
public DisplayThread(Object lockObj) {
this.lockObj = lockObj;
}
@Override
public void run() {
synchronized (lockObj) {
new TimeThread(lockObj).start();
try {
sleep(60000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TimeThread extends Thread {
Object lockObj;
public TimeThread(Object lockObj) {
this.lockObj = lockObj;
}
@Override
public void run() {
System.out.println("时间线程开始执行......");
synchronized (lockObj) {
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
String time = dateFormat.format(new Date());
System.out.println(time);//为什么这行代码60秒左右才会执行?
}
}
}
为什么这行代码60秒左右才会执行?
分析:
显示器线程和时间线程共享lockObj对象,显示器线程优先进入启动状态,随后执行相应的run方法,当执行同步代码块时lockObj变量所代表的对象锁归显示器线程所有,进而创建时间线程并使之处于启动状态,此时有一下两种状态:
1、时间线程马上进入执行状态,马上执行该时间线程run方法,可是由于此时lockObj变量所代表的对象锁被显示器线程持有,这时时间线程进入阻塞状态,显示器线程再次执行,然后执行sleep方法,显示器线程在继续持有对象锁的前提下也进入阻塞状态,60秒后显示器线程进入执行状态,随后显示器线程结束,对象锁被释放,进而时间线程开始执行,进而这行代码运行;
2、时间线程并没有马上进入执行状态,显示器线程执行sleep方法,显示器线程在继续持有对象锁的前提下也进入阻塞状态,此时时间线程进入执行状态,执行该时间线程run方法,执行该方法中第一行输出代码,可是由于此时lockObj变量所代表的对象锁被显示器线程持有,所以时间线程并没有执行时间线程run方法内临界区中的代码,这时时间线程也进入阻塞状态,此时显示器和时间两条线程均进去阻塞状态,等待少于60秒的时间后,显示器线程进入运行状态,随后显示器线程结束,对象锁被释放,进而时间线程开始执行,进而这行代码运行;
死锁:
- 如果有两个或两个以上的线程都访问了多个资源,而这些线程占用了一些资源的同时又在等待其它线程占用的资源,也就是说多个线程之间都持有了对方所需的资源,而又相互等待对方释放的资源,在这种情况下就会出现死锁。
- 多个线程互相等待对方释放对象锁,此时就会出现死锁
例程如下:
public class DeadLockThread {
// 创建两个线程之间竞争使用的对象
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new ShareThread1().start();
new ShareThread2().start();
}
private static class ShareThread1 extends Thread {
public void run() {
synchronized (lock1) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("ShareThread1");
}
}
}
}
private static class ShareThread2 extends Thread {
public void run() {
synchronized (lock2) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("ShareThread2");
}
}
}
}
}