关于java多线程由浅到深的学习(二)

前几天自己主要总结了下实现多线程的两种方式以及它们之间的区别,还有线程的状态。现在将继续整理多线程的其他特性。

一说到多线程,我们就想到了高并发下同步问题,现在就来了解下synchronized关键字的作用。

synchronized

在进一步阐述之前,我们需要明确几点: 
A.无论synchronized关键字加在方法上还是对象上,他取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。 
B.每个对象只有一个锁(lock)和之相关联。 
C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

使用范围

1、java中synchronized关键字能够用于函数的修饰。

public synchronized void enqueue(int i){
        System.out.println(i+Thread.currentThread().getName());
}
View Code

2、用于函数内部的语句,也就是平时说的同步与异步块。假如在细分的话,可以作用在下面这些上:

   (1)、instance变量

public class MyThread extends Thread{
    private byte[] by = new byte[0];
    
    
    public void method(){
        synchronized (by) {
            //逻辑代码
        }
    }
}
View Code

   (2)、object reference(对象引用)

public class MyThread extends Thread{
    
    public void method(Object obj){
        synchronized (obj) {
            //逻辑代码
        }
    }
}
View Code

 此时锁的就是obj这个对象,谁拿到这个锁谁就能够运行他所控制的那段代码,其他程序就只能堵塞。

 (3)、class literals(类名称字面常量)

public class MyThread extends Thread{
    
    public void method(){
        synchronized (MyThread.class) {
            //逻辑代码
        }
    }
}
View Code

   此时取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。

下面我们再来了解下synchronized(this)的一些特性。

 一、当多个并发线程同时访问同一对象object中的这个synchronized(this)同步代码块的时候,同一个时间点,只能一个线程在运行,而另一个线程等待。

public class RunThread implements Runnable {

    public void run() {
        synchronized (this) {
            for(int i=0;i<5;i++){
                System.out.println("当前线程为:"+i+Thread.currentThread().getName());
            }
        }
    }
    
    public static void main(String[] args) {  
        RunThread t1 = new RunThread();  
        Thread ta = new Thread(t1, "A");  
        Thread tb = new Thread(t1, "B");  
        ta.start();  
        tb.start();  
   } 
}
View Code

运行结果如下:

当前线程为:0B
当前线程为:1B
当前线程为:2B
当前线程为:3B
当前线程为:4B
当前线程为:0A
当前线程为:1A
当前线程为:2A
当前线程为:3A
当前线程为:4A
View Code

二、然而,当一个线程访问同一对象object中的synchronized(this)代码块的时候,另一线程可以访问非synchronized(this)的代码块。

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();  
     } 
}
View Code

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

 //修改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) {  
               }  
          }  
     }
View Code

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

五、以上规则对其它对象锁同样适用:

原文地址:https://www.cnblogs.com/duanzhiping/p/3588084.html