假如有个类:
public class resourceManage
{
   private int nums = 0;
   ...
   public synchronized void add()
   {
      nums++;
   }   public synchronized void subtract()
   {
      nums--;
   }
}就拿上面的代码来说, 如果一个线程在访问 add() 的时候, 另一个线程在同时能否访问 substract() ?

解决方案 »

  1.   

    不能 这个对象的所有资源都不能访问 即使这个资源不是synchronized 
      

  2.   

    同意ls
    synchronized 线程安全的
    其他不能访问
      

  3.   

    不能,因为add()方法中,使用了nums ,调用这个方法,就锁定了nums这个资源。1楼的说话,我不明白。
      

  4.   

    不能 这个对象的所有资源都不能访问 即使这个资源不是synchronized 
    ------
    当然不是,只有声明了synchronized的方法才会出现阻塞,其它所有方法是不会受到add正在被调用所影响,例如toString
      

  5.   

    可以
    每个函数每次只能允许一个线程访问,但两个之间互不相干,可以分别被线程访问
    如果你想做到每次只能访问一个,应该改成这样
    public class resourceManage 

       private int nums = 0; 
       ... 
       public void add() //这里可以不要synchronized 
       { 
          synchronized (this) { //这里synchronized
              nums++; 
          }
       }    public void subtract() //这里可以不要synchronized 
       { 
          synchronized (this) { //这里synchronized
              nums--; 
          }
       } 

    这样做是锁住对象本身,也就是每次只能有一个线程访问对象。你原来的做法只是锁住函数的入口,也就是每次只能有一个线程进入函数体,但是两个函数入口是互不影响的。
      

  6.   

    就拿最简单的wait和notifyAll的例子来看就知道了,一个线程在某一个函数中wait,另一个线程在另一个函数notifyAll,那就是两个线程分别访问两个函数的最基本的例子
      

  7.   

    不能,如果有一个方法有了synchronized ,那么这个类就是synchronized 了.下次不显示 [站长提示] 中资源100M虚拟主机只需200元/年,送:国际域名100元/年+企业邮箱150元/年!虚拟主机使用不满意,90天内可无条件退款!>>
    新云下载频道 网络技术学院 CDN镜像站点 电信镜像站点 用户登陆  |   用户注册首 页 ┆ 网络学院 ┆ 软件下载 ┆ 源码下载 ┆ 新云专区 ┆ 推荐软件 ┆ 源码排行 ┆ 软件排行 ┆ 最新源码 ┆ 最新软件 ┆ 新云论坛站点首页
     ASP编程
     .NET专区
     PHP编程
     Java编程
     数据库类
     网页设计
     服务器类
     编程开发
     图形设计
     软件教学
     安全相关
     网络软件 - 系统工具 - 应用软件 - 联络聊天 - 图形图像 - 多媒体类 - 行业软件 - 游戏娱乐 - 游戏娱乐 - 腾讯 QQ
    当前位置:新云网络 → Java编程 → Java技巧 → java 线程安全
    java 线程安全
    减小字体 增大字体 作者:佚名  来源:本站整理  发布时间:2005-7-1 4:38:30
    四种方式 sychronized关键字
    sychronized method(){} 
    sychronized (objectReference) {/*block*/} 
    static synchronized method(){} 
    sychronized(classname.class)
    其中1和2是代表锁当前对象,即一个对象就一个锁,3和4代表锁这个类,即这个类的锁
    要注意的是sychronized method()不是锁这个函数,而是锁对象,即:如果这个类中有两个方法都是sychronized,那么只要有两个线程共享一个该类的reference,每个调用这两个方法之一,不管是否同一个方法,都会用这个对象锁进行同步。锁类的3和4类推,即该类的不同reference调用了sychronized区段的咚咚就会受类锁的控制还有,如果两个函数调用的先后顺序不能被打断,那么可以有个专门的锁对象来完成这个任务:
    class MyLock
    {
    synchronized getLock()
    {
    //####还没写完
    }
    }
    五个等级 参见effective java Item 52 : Document thread safetyimmutable 不可变对象 
    thread-safe 线程安全的,可以放心使用,如java.util.Timer 
    conditionally thread-safe 条件线程安全的,如Vector和Hashtable,一般是安全的,除非存在几个方法调用之间的顺序不能被打断,这时可以用额外的锁来完成 
    thread-compatible 可以使用synchronized (objectReference)来协助完成对线程的调用 
    thread-hostile 不安全的wait & notifyAll在循环中使用wait 使用notifyAll而不是notify pipejava中也有pipe的,四个类:PipedInputStream, PipedInputReader, PipedOutputStream, PipedOutputWriter 下面是一段生产者消费者的代码(摘自core javaII):/* set up pipes */ 
    PipedOutputStream pout1 = new PipedOutputStream(); 
    PipedInputStream pin1 = new PipedInputStream(pout1); 
    PipedOutputStream pout2 = new PipedOutputStream(); 
    PipedInputStream pin2 = new PipedInputStream(pout2); 
    /* construct threads */ 
    Producer prod = new Producer(pout1); 
    Filter filt = new Filter(pin1, pout2); 
    Consumer cons = new Consumer(pin2); 
    /* start threads */ 
    prod.start(); filt.start(); cons.start(); 注意long 和double是简单类型中两个特殊的咚咚:java读他们要读两次,所以需要同步 
    死锁是一个经典的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉并轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁。假如线程 "A" 获得了刀,而线程 "B" 获得了叉。线程 A 就会进入阻塞状态来等待获得叉,而线程 B 则阻塞来等待 A 所拥有的刀。这只是人为设计的例子,但尽管在运行时很难探测到,这类情况却时常发生。虽然要探测或推敲各种情况是非常困难的,但只要按照下面几条规则去设计系统,就能够避免死锁问题:让所有的线程按照同样的顺序获得一组锁。这种方法消除了 X 和 Y 的拥有者分别等待对方的资源的问题。 将多个锁组成一组并放到同一个锁下。前面死锁的例子中,可以创建一个银器对象的锁。于是在获得刀或叉之前都必须获得这个银器的锁。 将那些不会阻塞的可获得资源用变量标志出来。当某个线程获得银器对象的锁时,就可以通过检查变量来判断是否整个银器集合中的对象锁都可获得。如果是,它就可以获得相关的锁,否则,就要释放掉银器这个锁并稍后再尝试。 最重要的是,在编写代码前认真仔细地设计整个系统。多线程是困难的,在开始编程之前详细设计系统能够帮助你避免难以发现死锁的问题。 
    Volatile 变量. volatile 关键字是 Java 语言为优化编译器设计的。以下面的代码为例:
    class VolatileTest {public void foo() {
    boolean flag = false;if(flag) {
    //this could happen
    }
    }
    }
    一个优化的编译器可能会判断出 if 部分的语句永远不会被执行,就根本不会编译这部分的代码。如果这个类被多线程访问, flag 被前面某个线程设置之后,在它被 if 语句测试之前,可以被其他线程重新设置。用 volatile 关键字来声明变量,就可以告诉编译器在编译的时候,不需要通过预测变量值来优化这部分的代码。
    无法访问的线程 有时候虽然获取对象锁没有问题,线程依然有可能进入阻塞状态。在 Java 编程中 IO 就是这类问题最好的例子。当线程因为对象内的 IO 调用而阻塞时,此对象应当仍能被其他线程访问。该对象通常有责任取消这个阻塞的 IO 操作。造成阻塞调用的线程常常会令同步任务失败。如果该对象的其他方法也是同步的,当线程被阻塞时,此对象也就相当于被冷冻住了。其他的线程由于不能获得对象的锁,就不能给此对象发消息(例如,取消 IO 操作)。必须确保不在同步代码中包含那些阻塞调用,或确认在一个用同步阻塞代码的对象中存在非同步方法。尽管这种方法需要花费一些注意力来保证结果代码安全运行,但它允许在拥有对象的线程发生阻塞后,该对象仍能够响应其他线程。 调用 yield() 方法能够将当前的线程从处理器中移出到准备就绪队列中。另一个方法则是调用 sleep() 方法,使线程放弃处理器,并且在 sleep 方法中指定的时间间隔内睡眠。 
    正如你所想的那样,将这些方法随意放在代码的某个地方,并不能够保证正常工作。如果线程正拥有一个锁(因为它在一个同步方法或代码块中),则当它调用 yield() 时不能够释放这个锁。这就意味着即使这个线程已经被挂起,等待这个锁释放的其他线程依然不能继续运行。为了缓解这个问题,最好不在同步方法中调用 yield 方法。将那些需要同步的代码包在一个同步块中,里面不含有非同步的方法,并且在这些同步代码块之外才调用 yield。另外一个解决方法则是调用 wait() 方法,使处理器放弃它当前拥有的对象的锁。如果对象在方法级别上使同步的,这种方法能够很好的工作。因为它仅仅使用了一个锁。如果它使用 fine-grained 锁,则 wait() 将无法放弃这些锁。此外,一个因为调用 wait() 方法而阻塞的线程,只有当其他线程调用 notifyAll() 时才会被唤醒。在进行多线程编程时,经常要使用同步互斥机构,但java本身没有提供的同步互斥机构,仅提供了两个与同步互斥有关的方法:wait()和notify(),可以用来设计信号量类:mySemaphore,它是按照Dijkstra提出的计数信号量的思想设计的。 mySemaphore有两个最重要的成员方法:P()和V()。这两个方法实际就实现了信号量的P操作和V操作。具体描述如下: public synchronized void P(){ semaphore--; if(semaphore<0){ try{ wait(); }catch(InterruptedException ie){} } } public synchronized void V(){ semaphore++; if(semaphore<=0) notify(); } 其中,semaphore变量记录了信号量的状态,wait()方法相当于block原语,用于阻塞线程的执行,notify()方法相当于wakeup原语,用于唤醒线程恢复运行。由于这两个方法定义为synchronized,这样java虚拟机可保证这两个方法的原子执行,从而实现了P、V操作。 二、管道 并发程序的多个线程之间的通讯通常是使用管道进行,jdk提供了两个管道类:PipedInpuStream和PipedOutputStream,前者用于输入,后者用于输出。这两种管道应该是能够多次连接和关闭,在实现过程中,却发现它们在关闭后,不能重新建立连接。经过仔细调试后,发现jdk的源代码在处理关闭时释放资源存在着缺陷,因此需要编写自己的管道类:MyPipedInputStream和MyPipedOutputStream。这两个类直接从InputStream和OutputStream继承而来,其成员方法与实现基本与PipedInputStream和PipedOutputStream一致,只是在处理关闭时,将类中的成员变量的值恢复成未连接时的初始值。另外,原有的管道了提供的管道容量只有1024个字节,在传输数据量较大时,可能会发生溢出,而在自己的管道类中可以任意设置管道容量,例如可以根据需要把管道容量设为64KB。以下仅给出了相应的关闭例程: 1.MyPipedInputStream public void close() throws IOException { in = -1; out = 0; closedByReader = true; connected = false; closed = true; buffer = new byte[PIPE_SIZE]; } 2.MyPipedOutputStream public void close() throws IOException { if (sink != null) { sink.receivedLast(); sink.closed = true; } sink = null; connected = false; } 
      

  8.   

    sychronized method(){}  
    sychronized (objectReference) {/*block*/}  
    static synchronized method(){}  
    sychronized(classname.class) 其中1和2是代表锁当前对象,即一个对象就一个锁,3和4代表锁这个类,即这个类的锁 
    要注意的是sychronized method()不是锁这个函数,而是锁对象,即:如果这个类中有两个方法都是sychronized,那么只要有两个线程共享一个该类的reference,每个调用这两个方法之一,不管是否同一个方法,都会用这个对象锁进行同步。锁类的3和4类推,即该类的不同reference调用了
    这里明明写的所得是对象怎么说是所函数了????。。public class resourceManage  
    {  
       private int nums = 0;  
       ...  
       public void add() //这里可以不要synchronized  
       {  
          synchronized (this) { //这里synchronized 
              nums++;  
          } 
       }     public void subtract() //这里可以不要synchronized  
       {  
          synchronized (this) { //这里synchronized 
              nums--;  
          } 
       }  
    }  这种做法和楼主的做法是一样的吧。锁的都是该实例。 
      

  9.   

    不能,public synchronized void add()和public synchronized void subtract()锁定的都是class resourceManage 即this,
    只有add()解锁后,subtract()才能锁定this
      

  10.   

    一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。五、以上规则对其它对象锁同样适用.举例说明:一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。package ths;public class Thread1 implements Runnable {
    public void run() {
    synchronized(this) {
    for (int i = 0; i < 5; i++) {
    System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
    }
    }
    }
    public static void main(String[] args) {
    Thread1 t1 = new Thread1();
    Thread ta = new Thread(t1, "A");
    Thread tb = new Thread(t1, "B");
    ta.start();
    tb.start();
    }
    }结果:A synchronized loop 0
    A synchronized loop 1
    A synchronized loop 2
    A synchronized loop 3
    A synchronized loop 4
    B synchronized loop 0
    B synchronized loop 1
    B synchronized loop 2
    B synchronized loop 3
    B synchronized loop 4二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。package ths;public class Thread2 {
    public void m4t1() {
    synchronized(this) {
    int i = 5;
    while( i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
    Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
    }
    }
    }
    public void m4t2() {
    int i = 5;
    while( i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
    Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
    }
    }
    public static void main(String[] args) {
    final Thread2 myt2 = new Thread2();
    Thread t1 = new Thread(
    new Runnable() {
    public void run() {
    myt2.m4t1();
    }
    }, "t1"
    );
    Thread t2 = new Thread(
    new Runnable() {
    public void run() {
    myt2.m4t2();
    }
    }, "t2"
    );
    t1.start();
    t2.start();
    }
    }结果:t1 : 4
    t2 : 4
    t1 : 3
    t2 : 3
    t1 : 2
    t2 : 2
    t1 : 1
    t2 : 1
    t1 : 0
    t2 : 0三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。//修改Thread2.m4t2()方法:public void m4t2() {
    synchronized(this) {
    int i = 5;
    while( i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
    Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
    }
    }}结果:t1 : 4
    t1 : 3
    t1 : 2
    t1 : 1
    t1 : 0
    t2 : 4
    t2 : 3
    t2 : 2
    t2 : 1
    t2 : 0四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。//修改Thread2.m4t2()方法如下:public synchronized void m4t2() {
    int i = 5;
    while( i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
    Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
    }
    }结果:t1 : 4
    t1 : 3
    t1 : 2
    t1 : 1
    t1 : 0
    t2 : 4
    t2 : 3
    t2 : 2
    t2 : 1
    t2 : 0五、以上规则对其它对象锁同样适用:package ths;public class Thread3 {
    class Inner {
    private void m4t1() {
    int i = 5;
    while(i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i);
    try {
    Thread.sleep(500);
    } catch(InterruptedException ie) {
    }
    }
    }
    private void m4t2() {
    int i = 5;
    while(i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
    try {
    Thread.sleep(500);
    } catch(InterruptedException ie) {
    }
    }
    }
    }
    private void m4t1(Inner inner) {
    synchronized(inner) { //使用对象锁
    inner.m4t1();
    }
    }
    private void m4t2(Inner inner) {
    inner.m4t2();
    }
    public static void main(String[] args) {
    final Thread3 myt3 = new Thread3();
    final Inner inner = myt3.new Inner();
    Thread t1 = new Thread(
    new Runnable() {
    public void run() {
    myt3.m4t1(inner);
    }
    }, "t1"
    );
    Thread t2 = new Thread(
    new Runnable() {
    public void run() {
    myt3.m4t2(inner);
    }
    }, "t2"
    );
    t1.start();
    t2.start();
    }
    }结果:尽管线程t1获得了对Inner的对象锁,但由于线程t2访问的是同一个Inner中的非同步部分。所以两个线程互不干扰。t1 : Inner.m4t1()=4
    t2 : Inner.m4t2()=4
    t1 : Inner.m4t1()=3
    t2 : Inner.m4t2()=3
    t1 : Inner.m4t1()=2
    t2 : Inner.m4t2()=2
    t1 : Inner.m4t1()=1
    t2 : Inner.m4t2()=1
    t1 : Inner.m4t1()=0
    t2 : Inner.m4t2()=0现在在Inner.m4t2()前面加上synchronized:private synchronized void m4t2() {
    int i = 5;
    while(i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
    try {
    Thread.sleep(500);
    } catch(InterruptedException ie) {
    }
    }
    }结果:尽管线程t1与t2访问了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁,所以t2对Inner.m4t2()的访问也被阻塞,因为m4t2()是Inner中的一个同步方法。t1 : Inner.m4t1()=4
    t1 : Inner.m4t1()=3
    t1 : Inner.m4t1()=2
    t1 : Inner.m4t1()=1
    t1 : Inner.m4t1()=0
    t2 : Inner.m4t2()=4
    t2 : Inner.m4t2()=3
    t2 : Inner.m4t2()=2
    t2 : Inner.m4t2()=1
    t2 : Inner.m4t2()=0 
      

  11.   

    怎么这么多人那么糊涂?理由我前面已经说了,不再重复。看看这个测试结果就知道了,AddThread进入add方法时,发生wait,也就是在synchronized add方法中发生等待(函数没结束还在函数里,一个wait对应一个wake up,这中间另一线程一直在调用sub方法),如果SubThread不能进入sub方法的话,谁来唤醒AddThread呢?那不就死锁了吗?下面的例子,AddThread打印出wait以后,SubThread还在继续打印,那就说明sub方法一直还被SubThread调用public class SynTest {
        int num = 0;
        static int count = 0;
        
        public static void main(String[] args) {
            try {
                SynTest obj = new SynTest();
                AddThread at = new AddThread(obj);
                SubThread st = new SubThread(obj);
                at.start();
                st.start();
                
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        
        public SynTest() {
            super();
        }
        
        public synchronized void add() {
            num += 3;
            System.out.println("add num=" + num);
            if (num > 10) {
                System.out.println("wait");
                try {
                    wait();
                    count++;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("wake up");
            }
        }
        
        public synchronized void sub() {
            num--;
            System.out.println("sub num=" + num);
            if (num<10) {
                System.out.println("notify all");
                notifyAll();
            }
        }
    }class AddThread extends Thread {
        SynTest obj;
        public AddThread(SynTest obj) {
            this.obj = obj;
        }
        
        public void run() {
            while (true) {
                if (SynTest.count > 2) {
                    break;
                }
                obj.add();
                yield();
            }
        }
    }class SubThread extends Thread {
        SynTest obj;
        public SubThread(SynTest obj) {
            this.obj = obj;
        }
        
        public void run() {
            while (true) {
                if (SynTest.count > 2) {
                    break;
                }
                
                obj.sub();
                try {
                    sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    锁住函数的入口,函数还是可以分别被不同的线程同时访问的(但每个方法只允许一个线程访问),只有锁住某个共同对象,线程才是一次执行。
      

  12.   

    记住一句话
    见到synchronized就该说“在什么对象上锁住了哪块代码”
    synchronized永远有两个要素,一个是什么对象,或者说什么锁,第二个是哪部分代码public synchronized function1(){
    num++;
    }等同于public function1()}{
    synchronized(this){
    num++;
    }
    }就是在this这个对象上加锁他不但和
    public synchronized function2(){
    num--;
    }
    同步,还和
    public function3(){
    System.out.println("");
    synchronized(this){
    num++;
    }
    }
    同步更有趣的是,如果function1,function2,function3都是一个对象obj的方法,如果另外一个类使用该对象,那么它还和如下代码同步class Other{
    ...public void testSynchronized(){
    synchronized(obj){
    ...
    }...
    }
    总而言之,只要见到synchronized就问一句“在什么对象上锁住了哪块代码”,如果对象相同,就是同步的
    明白否
      

  13.   

    fool_leave 和qybao 说的关于public synchronized function1(){ 
    num++; 
    } 等同于 public function1()}{ 
    synchronized(this){ 
    num++; 


    的两个有矛盾!!!
    fool_leave 意思是上面两种方法一样!
    qybao 意思是上面两种方法不一样!
    到低谁说的对?还是我没有弄明白?
      

  14.   

    好贴  顶  fool_leave    qybao    pwpw 
      

  15.   

    线程锁的只有两种,一种是锁对象,一种是锁类(声明为静态的static).
    fool_leave 说的是正确的,
    public synchronized function1(){  
    num++;  
    }  等同于  public function1()}{  
    synchronized(this){  
    num++;  
    }  
    }锁住的都是对象,这个时候是只允许一个线程访问 function1() 方法的.
    而qybao 所说的属于线程通信方面的问题,当调用wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。waite()和notify()因为会对对象的“锁标志”进行操作,所以它们必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。