同步和多线程
基本概念
在Java中,同步是一种控制多线程访问共享资源的能力。当我们只允许一个线程访问共享资源时同步是最好的选择。同步一般用来解决两个问题:1.防止线程干扰2.防止一致性问题。同步分两类:1.进程(Process)同步2.线程(Thread)同步。这里我们只讨论Java中线程同步。
当我们在一个程序中启动两个或两个以上线程时,可能会有一种情况:当多个线程试图访问相同的资源时由于并发问题会产生无法预料的结果。举个例子:如果多个线程同时写同一个文件它们可能会破坏数据,因为其中一个线程会覆盖数据,或者当一个线程打开文件的同时另一个线程正在关闭该文件。
所以多线程要考虑同步问题,确保同一时间只有一个线程可以访问资源。这里就会有一个概念叫监视器(monitors),每个对象(object)在Java中都关联一个监视器,每个线程都可以加锁(lock)和解锁(unlock),但同一时间只有一个线程可以给监视器加锁或解锁。
synchronized
synchronized(objectidentifier) {
// 访问共享变量和其它共享资源
}
这里的objectidentifier指的是获取了锁的监视器的对象。下来我们写两个例子,我们将使用两个不同的线程打印一个计数器。当没有线程同步时不是按顺序打印计数器的值,当使用synchronized()同步代码块时可以看到计数器打印的值是有序的。
多线程示例
没有同步代码块
这是一个简单的例子。它可能但不一定会按顺序打印出计数器的值,这是由CPU的可用性决定的。
class PrintDemo {
public void printCount() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Counter --- " + i );
}
}catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo PD;
ThreadDemo( String name, PrintDemo pd) {
threadName = name;
PD = pd;
}
public void run() {
PD.printCount();
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );
T1.start();
T2.start();
// wait for threads to end
try {
T1.join();
T2.join();
}catch( Exception e) {
System.out.println("Interrupted");
}
}
}
执行上面的代码每次运行的结果可能不一样,所以不能保证我们按顺序打印计数器。
使用同步代码块
下面是同样的一个例子,不过我们加了synchronized()同步代码块,这样它每次都能按顺序打印相同的值。
class PrintDemo {
public void printCount() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Counter --- " + i );
}
}catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo PD;
ThreadDemo( String name, PrintDemo pd) {
threadName = name;
PD = pd;
}
public void run() {
synchronized(PD) {
PD.printCount();
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );
T1.start();
T2.start();
// wait for threads to end
try {
T1.join();
T2.join();
}catch( Exception e) {
System.out.println("Interrupted");
}
}
}
执行上面的代码每次打印的结果都一样:按顺序输出计数器的值。
并发和并行
Java多任务处理都会有并发执行情况,多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一定的时间片运行。下面是并发和并行的区别:Concurrent and Parallel Programming
并发
两个队列一个咖啡机。一个CPU处理多个任务逻辑,通过CPU调度算法分配计算资源,非真正的同时进行。
并行
两个队列两个咖啡机。多个CPU处理多个任务逻辑,多个计算任务可以同时进行。
END