很多是基础知识,主要是实现的代码部分。把我的同步块的简单的例子写下来了~
1、如何实现多线程
实现多线程的方法主要有两种,一种是通过继承Thread类,重写run方法,然后实例化这个Thread的子类之后,调用start方法启动线程;另一种方法是实现Runnable接口,Runnable接口中只有一个run方法,实现这个抽象方法,然后再需要的地方,以这个实现了Runnable接口的类,为参数,构造一个Thread类,之后调用Thread的start方法,就可以启动线程进行后台任务了。
2、线程的一些属性
使用多线程时,有一些常用的属性需要掌握。例如,线程的状态、优先级等等。
线程的状态包括:可运行、不可运行、消亡、新线程状态。在Thread类被实例化时,线程是出于新线程状态的,还没有运行。调用start以后,线程出于可运行状态。如果在可运行状态,线程被阻塞或者在等待,那么就是出于不可运行状态,而run方法中的内容全部执行完成以后,线程就死亡了,已经消亡的线程无法再次执行。于此相关的常用的Thread的方法有:start,yield,isAlive方法等等。
线程的优先级是通过线程的几个字段来确定,即MAX_PRIORITY,NORM_PRIORITY
,MIN_PRIORITY。通过对优先级的设定,影响线程的调度问题。
线程还有一个重要的属性是守护线程即Daemon线程。守护线程的作用是在程序的运行期间于后台提供一种“常规”服务,但它并不属于程序的一个基本部分。因此,一旦所有非守护线程完成,程序也会中止运行。相反,假若有任何非守护线程仍在运行(比如还有一个正在运行main()的线程),则程序的运行不会中止。所以,在图形界面的情况下,在main方法中,通过
3、线程的同步
多线程的一个重要问题就是同步问题。对共享变量的访问和使用。(对硬盘中的文件,通常读写和同步是通过OS实现的。)对内存中的对象——Java提供了内建的机制来防止它们的冲突。由于我们通常将数据元素设为从属于private(私有)类,然后只通过方法访问那些内存,所以只需将一个特定的方法设为synchronized(同步的),便可有效地防止冲突。在任何时刻,只可有一个线程调用特定对象的一个synchronized方法(尽管那个线程可以调用多个对象的同步方法)。下面列出简单的synchronized方法:
synchronized void f() { }
synchronized void g() { }
每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。调用任何synchronized方法时,对象就会被锁定,不可再调用那个对象的其他任何synchronized方法,除非第一个方法完成了自己的工作,并解除锁定。在上面的例子中,如果为一个对象调用f(),便不能再为同样的对象调用g(),除非f()完成并解除锁定。因此,一个特定对象的所有synchronized方法都共享着一把锁,而且这把锁能防止多个方法对通用内存同时进行写操作(比如同时有多个线程)。每个类也有自己的一把锁(作为类的Class对象的一部分),所以synchronized
static方法可在一个类的范围内被相互间锁定起来,防止与static数据的接触。注意如果想保护其他某些资源不被多个线程同时访问,可以通过synchronized方法访问那些资源。
如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
在实现的时候,仅仅通过synchronized方法来实现同步也是不够的。尤其在需要线程进行暂停或者等待另一个线程完成固定任务后启动,这就需要人为的设置一些变量,这些变量也就是OS中同步、互斥的概念中的锁。通过标志变量的值,来考虑当前线程是等待还是执行。实现中,标志变量通常是采用boolean型的数据。
下面的另一个问题是,对标志变量的修改如何同步或者互斥?这时,就要用到synchronized块(同步块)了。用这个办法隔离出来的那部分代码叫作“关键区域”,而且要用不同的方式来使用synchronized关键字,以设置一个关键区域。Java通过同步块提供对关键区域的支持,像这样使用:
synchronized(synchObjectt) {
在能进入同步块之前,必须在synchObject上取得锁。如果已有其他线程取得了这把锁,块便不能进入,必须等候那把锁被释放。什么对象应作为锁来使用呢?这个就看实际的设计了。既可以使用当前类的实例,也可以新建一个对象专门作为锁来使用。在我的程序中,是使用了单独的锁的。我的程序就是这样实现的。如下面简略的代码。
public class Test implements Runnable{
//这是两个标志变量
private static volatile Boolean SUSPEND =
false;
private static volatile Boolean STOP =
false;
//这是标志变量的锁
private static final Object SUSPEND_LOCK = new
Object();
private static final Object STOP_LOCK = new
Object();
public void run() {
if (STOP)
{
return
null;
}
//这是暂停,线程阻塞
while
(SUSPEND) {
synchronized (SUSPEND_LOCK) {
SUSPEND_LOCK.wait(2000);
}
}
public void start() {
synchronized (STOP_LOCK) {
STOP =
false;
STOP_LOCK.notifyAll();
}
}
public void stop() {
synchronized (STOP_LOCK) {
STOP =
true;
STOP_LOCK.notifyAll();
}
}
public void suspend() {
synchronized (SUSPEND_LOCK) {
SUSPEND =
true;
SUSPEND_LOCK.notifyAll();
}
}
public void resume() {
synchronized (SUSPEND_LOCK) {
SUSPEND =
false;
SUSPEND_LOCK.notifyAll();
}
}
}
关于多线程同步的一些其他注意的问题:
1)、只能同步方法,而不能同步变量和类;
2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
3)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
4)、线程睡眠时,它所持的任何锁都不会释放。
5)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
6)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。