线程入门-线程同步浅析

前言

刚实习的时候,当遇到数据量大并对效率要求高的业务时,就开始尝试学习如何使用多线程来处理。现在与大家分享一下。大家说到多线程,总有一个绕不开的问题,就是如何实现多线程的同步。大致总结了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很适合处理类似文件的读写操作。
     * 读的时候可以同时读,但不能写;写的时候既不能同时写也不能读。
     */  
时间: 2024-10-24 10:29:18

线程入门-线程同步浅析的相关文章

Java线程:线程的同步-同步方法

线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有竞争资源被同时改动的问题? 在本文之前,请参阅<Java线程:线程的同步与锁>,本文是在此基础上所写的. 对于同步,在具体的Java代码中需要完成一下两个操作: 把竞争访问的资源标识为private: 同步哪些修改变量的代码,使用synchronized关键字同步方法或代码. 当然这不是唯一控制并发安全的途径. synch

Java线程:线程的同步-同步块

追其同步的根本的目的,是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问即可,因此Java引入了同步代码快的策略,以提高性能. 在上个例子的基础上,对oper方法做了改动,由同步方法改为同步代码块模式,程序的执行逻辑并没有问题. /** * Java线程:线程的同步-同步代码块 * * @author leizhimin */ public class Test { public static void main(String[] args) { User u

Java基础-23总结多线程,线程实现Runnable接口,线程名字获取和设置,线程控制,线程安全,同步线程

你需要的是什么,直接评论留言. 获取更多资源加微信公众号"Java帮帮" (是公众号,不是微信好友哦) 还有"Java帮帮"今日头条号,技术文章与新闻,每日更新,欢迎阅读 学习交流请加Java帮帮交流QQ群553841695 分享是一种美德,分享更快乐! 1:多线程(理解) (1)多线程:一个应用程序有多条执行路径 进程:正在执行的应用程序 线程:进程的执行单元,执行路径 单线程:一个应用程序只有一条执行路径 多线程:一个应用程序有多条执行路径 多进程的意义? 提高

为什么使用了volatile关键字,两个线程还是没有同步成功!求救

问题描述 为什么使用了volatile关键字,两个线程还是没有同步成功!求救publicclassTestVolatileimplementsRunnable{privatevolatileinti=0;@Overridepublicvoidrun(){while(i<10){if("bbb".equals(Thread.currentThread().getName())&&i==3){i=11;}System.out.println("hellowo

一起谈.NET技术,关于C#线程,线程池和并行运算的简单使用和对比

前言:看了书上两个使用C#4.0并行编程的demo,又对照以前收藏的网上几篇讲述线程池的雄文,一并整理,写个示例总结一下.写这篇文章的时候,发现关于线程的好几个基础的重要的知识点自己都不熟悉,而且可能习惯性认知浅薄,所以痛苦的无以复加,不知道到底要说什么.不想看文章的可以直接下载最后的示例,本文代码主要参考Marc Clifton的".NET's ThreadPool Class - Behind The Scenes",对新手也许有帮助. 参考: http://msdn.micros

浅谈Android 的线程和线程池的使用

Android 的线程和线程池 从用途上分,线程分为主线程和子线程:主线程主要处理和界面相关的事情,子线程则往往用于耗时操作. 主线程和子线程 主线程是指进程所拥有的线程.Android 中主线程交 UI 线程,主要作用是运行四大组件以及处理它们和用户的交互:子线程的作业则是执行耗时任务. Android 中的线程形态 1.AsyncTask AsyncTask 是一种轻量级的异步任务类,可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新 UI, AsyncTas

java web应用中新起一个线程 在线程中怎么获取系统登录的数据

问题描述 java web应用中新起一个线程 在线程中怎么获取系统登录的数据 java web应用中新起一个线程 在线程中怎么获取系统登录的数据,系统登录用的是Spring Secrity 解决方案 可以使用缓存,自己写一个缓存类.或者使用Redis.

Java游戏起步:(一)线程与线程池

任何游戏都至少需要运行两个线程,主线程和GUI线程而线程池是一个管理运行线程的有用工具,下面的代码示范了一个线程池的实现方法~~************************************************(ThreadPool.java)import java.util.LinkedList; /** 线程池是一组线程,限制执行任务的线程数*/public class ThreadPool extends ThreadGroup { private boolean isAli

J2SE5.0中的线程缓冲 ---- 线程池

一.前言 用Java编写多线程程序已经是一个非常简单的事了,不过与其它多线程系统相比,一些高级特性在Java中仍然不具备,然而在J2SE5.0中这一切将会改变.J2SE5.0增加大量的线程相关类使得编写多线程程序更加容易! 二.线程池-Thread Pools 线程库的基本思想简单的讲就是,一个线程库中拥有一定数量的线程,当有任务要执行时,就从线程库中找一个空闲的线程来执行这个任务,任务执行完后,该线程返回线程库等待下一个任务;如果线程库中没有空闲的线程来执行该任务,这时该任务将要等待直到有一个