线程入门-线程同步浅析

简介: #前言 刚实习的时候,当遇到数据量大并对效率要求高的业务时,就开始尝试学习如何使用多线程来处理。现在与大家分享一下。大家说到多线程,总有一个绕不开的问题,就是如何实现多线程的同步。大致总结了2个大家常用的方式:synchronized关键字与java.util.concurrent.locks.Lock接口。 #synchronized关键字 synchronized关键

前言

刚实习的时候,当遇到数据量大并对效率要求高的业务时,就开始尝试学习如何使用多线程来处理。现在与大家分享一下。大家说到多线程,总有一个绕不开的问题,就是如何实现多线程的同步。大致总结了2个大家常用的方式:synchronized关键字与java.util.concurrent.locks.Lock接口。

synchronized关键字

synchronized关键字一般作用于代码块或者方法。根据场景又有所不同。

synchronized作用于代码块时

synchronized(this)

这种方式锁的是当前的对象,如以下代码

package thread;

      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); 
                         try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                    }  
               }  
          }  
          public static void main(String[] args) {  
               Thread1 target = new Thread1();  
               Thread threadA = new Thread(target, "threadA");  
               Thread threadB = new Thread(target, "threadB");  
               threadA.start();  
               threadB.start(); 
          } 
      }
  
      输出结果:

      threadA synchronized loop 0
      threadA synchronized loop 1
      threadA synchronized loop 2
      threadA synchronized loop 3
      threadA synchronized loop 4
      threadB synchronized loop 0
      threadB synchronized loop 1
      threadB synchronized loop 2
      threadB synchronized loop 3
      threadB synchronized loop 4

synchronized(object)

这种方式锁的是object存在heap中的内容,而非stack上的引用.我们来看两段代码

package thread;

public class Thread2 implements Runnable {
    private Vsrsion version;

    public void run() {
        synchronized (version) {
            for (int i = 0; i < 5; i++) {
                //version = new Vsrsion(i);
                version.content = i;
                System.out.println(Thread.currentThread().getName() + " synchronized loop " + i + "version" + version.content);
            }
        }
    }

    public static void main(String[] args) {
         Vsrsion a = new Vsrsion(0);
         Thread2 target = new Thread2(); 
         target.version = a;
         Thread threadA = new Thread(target, "threadA");  
         Thread threadB = new Thread(target, "threadB");  
         threadA.start();  
         threadB.start(); 
    }
}

class Vsrsion {
    int content;
    public Vsrsion(int content){
        this.content = content;
    }
}
输出结果:
threadA synchronized loop 0version0
threadA synchronized loop 1version1
threadA synchronized loop 2version2
threadA synchronized loop 3version3
threadA synchronized loop 4version4
threadB synchronized loop 0version0
threadB synchronized loop 1version1
threadB synchronized loop 2version2
threadB synchronized loop 3version3
threadB synchronized loop 4version4

因为2线程个线程是只是对version对象内的值修改,所以2个线程有序进行执行。

package thread;

public class Thread2 implements Runnable {
    private Vsrsion version;

    public void run() {
        synchronized (version) {
            for (int i = 0; i < 5; i++) {
                version = new Vsrsion(i);
                //version.content = i;
                System.out.println(Thread.currentThread().getName() + " synchronized loop " + i + "version" + version.content);
            }
        }
    }

    public static void main(String[] args) {
         Vsrsion a = new Vsrsion(0);
         Thread2 target = new Thread2(); 
         target.version = a;
         Thread threadA = new Thread(target, "threadA");  
         Thread threadB = new Thread(target, "threadB");  
         threadA.start();  
         threadB.start(); 
    }
}

class Vsrsion {
    int content;
    public Vsrsion(int content){
        this.content = content;
    }
}

输出结果:

threadB synchronized loop 0version0
threadB synchronized loop 1version1
threadA synchronized loop 0version0
threadB synchronized loop 2version2
threadB synchronized loop 3version3
threadA synchronized loop 1version1
threadB synchronized loop 4version4
threadA synchronized loop 2version2
threadA synchronized loop 3version3
threadA synchronized loop 4version4

从这里就能看出区别来了,因为线程里面的循环在heap创建新的对象,并将新的地址付给stack上的引用。所以2个线程其实只同步了第一次的version0,2个线程最后就乱序执行了。

synchronized何时产生作用

当一个synchronized(object)锁住object时,非synchronized代码块照样能操作这段代码块

package thread;

public class Thread2 implements Runnable {
    private Vsrsion version;

    public void run() {
        synchronized (version) {
            for (int i = 0; i < 5; i++) {
                version.content = i;
                try {
                    Thread.sleep((long) (Math.random()*100));
                } catch (InterruptedException e) {
                }
                System.out.println(Thread.currentThread().getName() + " version " + version.content);
            }
        }
    }

    public static void main(String[] args) {
         Vsrsion a = new Vsrsion(0);
         Thread2 target = new Thread2(); 
         target.version = a;
         Thread threadA = new Thread(target, "threadA");  
         threadA.start();  
         
         for (int i = 0; i<5; i++ ){
             a.content = i;
             System.out.println("loop version " + a.content);
             try {
                 Thread.sleep((long) (Math.random()*100));
             } catch (InterruptedException e) {
             }
         }
    }
}

class Vsrsion {
    int content;
    public Vsrsion(int content) {
        this.content = content;
    }
}

输出结果:

loop version 0
threadA version 0
loop version 1
threadA version 1
loop version 2
threadA version 2
loop version 3
loop version 4
threadA version 4
threadA version 4

所以当多个线程对某一资源做同步时,必须都加上synchronized关键字

1.2当synchronized作用于方法时

1.2.1.当方法为静态

例如:

class A{
    public static synchronized void medthod(){}
}
并等价于
class A{
    public void medthod(){
        synchonized(A.class){
            ....................
         }
    }
}

并未创建A对象并且要同步A中的static变量时,可以使用这种方式

1.2.2.该方法为非静态

package thread;

public class Thread3 implements Runnable {

    public synchronized void print(){
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
            try {
                Thread.sleep((long) (Math.random()*100));
            } catch (InterruptedException e) {
            }
        }
    }
    
    public void run() {
        print();
    }

    public static void main(String[] args) {
         Thread3 target = new Thread3(); 
         Thread threadA = new Thread(target, "threadA");  
         Thread threadB = new Thread(target, "threadB");  
         threadA.start();  
         threadB.start(); 
    }
}

输出结果:
threadA synchronized loop 0
threadA synchronized loop 1
threadA synchronized loop 2
threadA synchronized loop 3
threadA synchronized loop 4
threadB synchronized loop 0
threadB synchronized loop 1
threadB synchronized loop 2
threadB synchronized loop 3
threadB synchronized loop 4

保证了这个类的方法在多个线程内得到同步。把 public synchronized void print()改成public static synchronized void print()结果不变。

java.util.concurrent.locks.Lock

关于这个接口的三个实现类,这里简单的讲下

ReentrantLock, 
Lock.lock()
Lock.unlock()
这个锁保证只有一个线程能对这块加锁的代码进行读和写操作

ReentrantReadWriteLock.ReadLock, 
lock.readLock()
lock.unlock()

ReentrantReadWriteLock.WriteLock
lock.wirteLock()
lock.unlock()
   /**
     * ReadWriteLock内置两个Lock,一个是读的Lock,一个是写的Lock。
     * 多个线程可同时得到读的Lock,但只有一个线程能得到写的Lock,
     * 而且写的Lock被锁定后,任何线程都不能得到Lock。ReadWriteLock提供的方法有:
     * readLock(): 返回一个读的lock 
     * writeLock(): 返回一个写的lock, 此lock是排他的。
     * ReadWriteLockTest很适合处理类似文件的读写操作。
     * 读的时候可以同时读,但不能写;写的时候既不能同时写也不能读。
     */  
目录
相关文章
|
29天前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
58 0
|
5月前
|
监控 安全 算法
Thread入门与线程方法详解及多线程安全
Thread入门与线程方法详解及多线程安全
21 0
|
6月前
|
缓存 安全 Java
【JavaSE专栏78】线程同步,控制多个线程之间的访问顺序和共享资源的安全性
【JavaSE专栏78】线程同步,控制多个线程之间的访问顺序和共享资源的安全性
|
3月前
|
数据处理
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
43 1
|
3月前
|
设计模式 监控 安全
多线程设计模式【多线程上下文设计模式、Guarded Suspension 设计模式、 Latch 设计模式】(二)-全面详解(学习总结---从入门到深化)
多线程设计模式【多线程上下文设计模式、Guarded Suspension 设计模式、 Latch 设计模式】(二)-全面详解(学习总结---从入门到深化)
62 0
|
3月前
|
算法 小程序 Java
多线程与并发编程【多线程与并发编程、 进程、线程的区别、 线程的创建】(一)-全面详解(学习总结---从入门到深化)
多线程与并发编程【多线程与并发编程、 进程、线程的区别、 线程的创建】(一)-全面详解(学习总结---从入门到深化)
42 1
|
2月前
|
Java 调度
详解线程同步和线程互斥,Java如何实现线程同步和互斥
详解线程同步和线程互斥,Java如何实现线程同步和互斥
25 0
|
3月前
|
设计模式 安全 Java
多线程设计模式【线程安全、 Future 设计模式、Master-Worker 设计模式 】(一)-全面详解(学习总结---从入门到深化)
多线程设计模式【线程安全、 Future 设计模式、Master-Worker 设计模式 】(一)-全面详解(学习总结---从入门到深化)
28 0
|
3月前
|
Java 数据安全/隐私保护 块存储
多线程与并发编程【守护线程、线程同步】(三)-全面详解(学习总结---从入门到深化)
多线程与并发编程【守护线程、线程同步】(三)-全面详解(学习总结---从入门到深化)
37 1
|
3月前
|
Java 调度
多线程与并发编程【线程休眠、线程让步、线程联合、判断线程是否存活】(二)-全面详解(学习总结---从入门到深化)
多线程与并发编程【线程休眠、线程让步、线程联合、判断线程是否存活】(二)-全面详解(学习总结---从入门到深化)
29 1