Java多线程[3]:线程同步(互斥)

来源:互联网 发布:仿商城html5源码 编辑:程序博客网 时间:2024/04/29 17:25

由于Java中的多线程写起来比较长,为了使博客读起来更加简洁,我决定将java多线程部分拆分开来写。本篇是第三篇,线程同步,也成为线程互斥。

什么是线程同步

当两个或多个线程需要访问共享资源时,它们需要以某种方式确保每次只有一个线程使用资源,实现这一目的的过程称为线程同步。Java为线程同步提供了很好的支持。

监视器的概念常用来解决线程同步问题。监视器是用做互斥锁的对象。在任何时刻,只有一个线程可以拥有监视器。当线程取得锁时,也就进入了监视器。其它所有企图进入加锁监视器的线程都会被挂起,直到第一个线程退出监视器。

在Java中,可以使用两种方法来实现线程之间的同步,这两种方法都会用到synchronized关键字。

  1. 同步方法
  2. 同步语句块

不使用线程同步导致的问题

首先我们先给出一个应该使用线程同步而没有使用的应用场景。
首先,我们定义一个工具类叫PrintUtil,用来实现打印输出的功能

public class PrintUtil {        public static void Print(String content) {        System.out.print("[" + content);        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("]");    }}

然后定义一个类NewThread,实现Runnable接口,这个类在run方法中调用了PrintUtil.Print()方法。

public class NewThread implements Runnable {    Thread t;    String message;    public NewThread(String msg) {        this.message = msg;        t = new Thread(this, "my test thread");        t.start();    }    @Override    public void run() {        PrintUtil.Print(message);    }}

下面是main方法。

public class Program {    public static void main(String[] args) {        NewThread obj1= new NewThread("hello");        NewThread obj2= new NewThread("java");        NewThread obj3= new NewThread("servlet");        try {            obj1.t.join();            obj2.t.join();            obj3.t.join();        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

在main方法中,实例化了三个NewThread对象,也就启动了三个线程,每个线程的run方法都调用了PrintUtil.Print() 方法。由于没有做线程同步的处理,输出的结果如下:

[hello[java[servlet]]]

很明显,这不是我们想要的结果

使用同步方法来实现线程同步

我们必须要限制每次只能由一个线程的run方法来调用Print()方法,并且当该方法执行完毕之后下一个线程才能调用。在Java中,想做到这个是非常容易的,只需要在PrintUtil.Print() 方法前面加上一个关键字synchronized就能轻松搞定。修改后的PrintUtil类的代码如下

public class PrintUtil {    synchronized public static void Print(String content) {        System.out.print("[" + content);        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("]");    }}

其它的地方无需修改。重新运行程序,结果如下:

[hello][servlet][java]

输出的顺序可能会不一样,但左右括号都是完整的。

使用同步语句块来实现线程同步

虽然在类中创建synchronized方法是一种比较容易并且行之有效的实现线程同步的方法,但是,它并不能适用于所有场景。例如:假设PrintUtil类不是由你设计的,你也不能修改它,肿么办?同步语句块诞生了!
首先,我们将PrintUtil.Print() 方法前面的synchronized关键字去掉,然后假装我们没有权限修改这个类。然后,我们来修改NewThread的run方法,修改后的NewThread类的代码如下

public class NewThread implements Runnable {    Thread t;    String message;    //这个对象其实没有卵用,只是为了对它进行加锁,因为这个对象对于多个线程来说是同一个引用    static Object obj = new Object();    public NewThread(String msg) {        this.message = msg;        t = new Thread(this, "my test thread");        t.start();    }    @Override    public void run() {             synchronized (obj) {            PrintUtil.Print(message);        }    }}

重新运行代码,输出的结果是

[hello][servlet][java]

下面说一下synchronized关键字后面的括号中的对象。这个对象对于多个线程来说,必须是同一个引用,通常情况下是类外部的一个对象。如果在类内部的话,必须声明为静态的。总之,它不能属于当前线程对象,要么属于外部,要么属于当前类(静态)。

0 0
原创粉丝点击