Java中同步线程的实现

时间:2022-09-15 12:09:43

[摘要] 本文介绍了线程及其运行机理,探讨了Java线程的设计方法,并就Java线程设计中的关键问题即同步线程的实现问题提出了四种解决方案。

[关键词] 线程 多线程 同步

一、线程相关概念

所谓“线程”(Thread),是“进程”中某个单一顺序的控制流。而多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信较进程简单。

多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。

多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度、同步等问题,接下来我们就对Java中对线程设计与同步问题进行探讨。

二、线程的设计方法

创建线程主要有两种形式:一种是继承自Thread类。要创建一个线程,必须创建一个从 Thread 类导出的新类。通过覆盖 Thread的run()函数来完成有用的工作。但用户通过调用Thread的start() 函数调用run()。

例:

public class Ctest extends Thread

{

public Ctest()

{…}

public static void main(String args[])

{Ctest t1=new Ctest();

t1.start();}

public void run()

{….}

}

另一种是实现Runnable接口,此接口只有一个函数run(),它由实现此接口的类实现。

例:

public class Ctest implements Runnable

{ Thread thread1;

public Ctest()

{ thread1=new Thread(this,"1");}

public static void main(String args[])

{Ctest t=new Ctest();

t.startThreads(); }

public void run()

{…}

public void startThreads()

{ thread1.start(); }

}

两种创建方式差别不大,第一种因为继承自Thread,只创建了自身对象;第二种还得创建Thread对象。但是当你想继承某一其他类时,只能用后一种方式。

三、同步线程实现

线程的创建虽然只有两种形式,但是利用这两种形式我们可以在同一进程中设计多个线程;多个线程并发执行,极大地提高的程序的运行效率。但是许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态,否则会出现冲突。

解决冲突的很好办法就是在设计中引入同步机制。在Java中每个对象都有一把锁与之对应。但Java不提供单独的lock和unlock操作。它由高层的结构隐式实现,,保证操作的对应。

synchronized语句计算一个对象引用,试图对该对象完成锁操作,并在完成锁操作前停止处理。当锁操作完成synchronized语句体得到执行;当语句体执行完毕(无论正常或异常),解锁操作自动完成。作为面向对象的语言,synchronized经常与方法连用。一种比较好的办法是,如果某个变量由一个线程赋值并由别的线程引用或赋值,那么所有对该变量的访问都必须在某个synchronized语句或方法内,具体方法如下:

(1)方法声明时使用,放在范围操作符之后,返回类型声明之前。

这时线程获得的是成员锁,即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程执行完该方法后,别的线程才能进入。

例:public synchronized void synMethod()

(2)对某一代码块使用,synchronized后跟括号,括号里是变量,一次只有一个线程进入该代码块。此时,线程获得的是成员锁。例:

public int synMethod(int a1)

{synchronized(a1)

}

(3)synchronized后面括号里是一对象,此时线程获得的是对象锁。

例:

public class MyThread implements Runnable

{public static void main(String args[])

{MyThread mt=new MyThread();

Thread t2=new Thread(mt, "t2");

t2.start();

}

public void run()

{synchronized (this)

{System.out.println(Thread.currentThread().getName());}

}}

对于(3),如果线程进入,则得到当前对象锁,那么别的线程在该类所有对象上的任何操作都不能进行。

(4)synchronized后面括号里是类,此时,线程获得的是对象锁,例:

class ArrayWithLockOrder{

private static long num_locks=0;

private long lock_order;

private int[] arr;

public ArrayWithLockOrder(int[] a)

{arr=a;

synchronized(ArrayWithLockOrder.class){

num_locks++;

lock_order=num_locks;

}

}

public long lockOrder()

{return lock_order;}

public int[] array()

{return arr;}

}

class SomeClass implements Runnable

{public int sumArrays(ArrayWithLockOrder a1,ArrayWithLockOrder a2)

{int value = 0;

ArrayWithLockOrder first = a1;

ArrayWithLockOrder last = a2;

int size=a1.array().length;

if (size==a2.array().length)

{

if (a1.lockOrder()>a2.lockOrder())

{first=a2;

last=a1;}

synchronized(first){

synchronized(last){

int[] arr1=a1.array();

int[] arr2=a2.array();

for(int i=0;i

value+=arr1[i]+arr2[i];

}}}

return value;

}

public void run()

{...}

}

对于(4),如果线程进入,则线程在该类中所有操作不能进行,包括静态变量和静态方法。对于含有静态方法和变量的代码块的同步,常用(4)来加锁。

四、小结

多线程机制可以同时运行好几个程序块,使程序运行的效率变得更高,也可克服传统程序语言无法设计的问题,其设计的关键在于同步线程的实现。除此之外,还需检查所有可能Block的地方,尽可能多的使用sleep或yield( )以及wait( ),尽可能延长sleep的时间,运行的线程不用超过100个,使多线程真正服务于程序员。

本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。

上一篇:流域水利现代化的动力评价探讨 下一篇:物流信息系统中的数据集成技术