Java线程安全synchronize学习

Java中,synchronized关键字有2种用法:

  1. 作为关键字修饰方法
  2. 修饰一个代码块

[TOC]

线程争用

为了探究synchronized的具体用法,可以用一个简单的程序来说明:

package fc.learn.java.synchronize;

import java.util.Random;

public class LearningSynchronized {

    public enum SyncTypeTag {
        OBJ_METHOD, CLASS_METHOD, KEYWARD_METHOD, NORMAL_METHOD
    }

    static class MyRunnable implements Runnable{

        private void counting(){
            for (int i = 0; i< 5; i++){
                System.out.println(String.format("Thread[%s] count for [%s]", Thread.currentThread().getName(), i));
                int sleepSeconds = new Random().nextInt();
                sleepSeconds = sleepSeconds % 10;
                sleepSeconds = sleepSeconds * sleepSeconds;
                sleepSeconds = sleepSeconds % 5;
                System.out.println(String.format("Thread[%s] sleep for 0.%s seconds", Thread.currentThread().getName(), sleepSeconds));
                try {
                    Thread.sleep(sleepSeconds * 100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        private void synchronizeObjectMethod() {
            synchronized (this){
                counting();
            }
        }

        private void synchronizeClasstMethod(){
            synchronized (MyRunnable.class){
                counting();
            }
        }

        private synchronized void synchronizedMethod(){
            counting();
        }

        private void normalMethod(){
            counting();
        }

        private SyncTypeTag synType;
        public MyRunnable(SyncTypeTag type){
            this.synType = type;
        }

        @Override
        public void run() {
            if (this.synType == SyncTypeTag.OBJ_METHOD) {
                synchronizeObjectMethod();
            } else if (this.synType == SyncTypeTag.CLASS_METHOD) {
                synchronizeClasstMethod();
            } else if (this.synType == SyncTypeTag.KEYWARD_METHOD) {
                synchronizedMethod();
            } else if (this.synType == SyncTypeTag.NORMAL_METHOD) {
                normalMethod();
            }
        }
    }

    public static void main(String[] args) {
        Runnable r1 = new MyRunnable(SyncTypeTag.NORMAL_METHOD);
        Runnable r2 = new MyRunnable(SyncTypeTag.NORMAL_METHOD);

        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();
    }

}

运行代码,可以获得结果如下:

Thread[Thread-1] count for [0]
Thread[Thread-0] count for [0]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [3]
Thread[Thread-0] count for [2]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.1 seconds

也就是说,普通情况下,counting()方法会被线程争用

类锁 synchronized(class)

那么我们加锁试试呢?首先试试对class加锁:

// 之前代码太多不再赘述,此处只展示main方法
public static void main(String[] args) {
    Runnable r1 = new MyRunnable(SyncTypeTag.CLASS_METHOD);
    Runnable r2 = new MyRunnable(SyncTypeTag.CLASS_METHOD);

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);

    t1.start();
    t2.start();
}

可以得到如下结果:

Thread[Thread-0] count for [0]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-0] count for [2]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [0]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.1 seconds

此时,线程不再争用,目的达到。

对象锁 synchronized(object)

那么试试对象锁:

// 仔细观察,这里用的 this 也就是说,每个线程用的不是同一把锁
private void synchronizeObjectMethod() {
    synchronized (this){
        counting();
    }
}

public static void main(String[] args) {
    Runnable r1 = new MyRunnable(SyncTypeTag.CLASS_METHOD);
    Runnable r2 = new MyRunnable(SyncTypeTag.CLASS_METHOD);

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);

    t1.start();
    t2.start();
}

运行代码:

Thread[Thread-1] count for [0]
Thread[Thread-0] count for [0]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [2]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.0 seconds

可以发现,锁并不起作用。这个原因也很容易理解,因为synchronizeObjectMethod方法中用的synchronized(this)进行加锁,而我们有2个进程对象在对counting()方法进行操作,所以会发生争用。如果代码修改为这样:

// synchronizedObjectMethod修改成这样:
private void synchronizeObjectMethod(Object globalLock) {
    synchronized (globalLock){
        counting();
    }
}
// 构造方法和成员变量:
private SyncTypeTag synType;
private Object globalLock;
public MyRunnable(SyncTypeTag type, Object lock){
    this.synType = type;
    this.globalLock = lock;
}

@Override
public void run() {
    if (this.synType == SyncTypeTag.OBJ_METHOD) {
        synchronizeObjectMethod(this.globalLock);
    } else if (this.synType == SyncTypeTag.CLASS_METHOD) {
        synchronizeClasstMethod();
    } else if (this.synType == SyncTypeTag.KEYWARD_METHOD) {
        synchronizedMethod();
    } else if (this.synType == SyncTypeTag.NORMAL_METHOD) {
        normalMethod();
    }
}

// main 方法:
public static void main(String[] args) {
    Object globalLock = new Object();
    Runnable r1 = new MyRunnable(SyncTypeTag.OBJ_METHOD, globalLock);
    Runnable r2 = new MyRunnable(SyncTypeTag.OBJ_METHOD, globalLock);

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);

    t1.start();
    t2.start();
}

运行代码:

Thread[Thread-0] count for [0]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [2]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-1] count for [0]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.4 seconds

争用消失。

关键字 synchronized method()

接下来再试试synchronized关键字直接修饰方法:

public static void main(String[] args) {
    Object globalLock = new Object();
    Runnable r1 = new MyRunnable(SyncTypeTag.KEYWARD_METHOD, globalLock);
    Runnable r2 = new MyRunnable(SyncTypeTag.KEYWARD_METHOD, globalLock);

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);

    t1.start();
    t2.start();
}

运行代码:

Thread[Thread-0] count for [0]
Thread[Thread-1] count for [0]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-1] count for [1]
Thread[Thread-0] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.1 seconds

依旧会起争用,说明synchronized修饰方法,等于 synchronized(this)

References:

http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html

原文地址:https://www.cnblogs.com/tflowingcloud/p/8428743.html

时间: 2024-08-03 18:54:14

Java线程安全synchronize学习的相关文章

java线程池的学习

package advancedJava;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit; * java 线程池学习 * @author: cuiH * Date: 13-12-7public class ThreadPoolTest { * 线程池的概念 * jdk5提出了ThreadPool的概念 * 之

【Java线程池快速学习教程】

1. Java线程池 线程池:顾名思义,用一个池子装载多个线程,使用池子去管理多个线程. 问题来源:应用大量通过new Thread()方法创建执行时间短的线程,较大的消耗系统资源并且系统的响应速度变慢.[在一个什么程度上能够判断启用线程池对系统的资源消耗比启动定量的new Thread()资源消耗低?这个怎么测试?][用户体验卡顿?慢?观察CPU百分比?] 解决办法:使用线程池管理短时间执行完毕的大量线程,通过重用已存在的线程,降低线程创建和销毁造成的消耗,提高系统响应速度. 2. Java线

java线程的中断学习

<span style="font-size:18px;">package thread.java.test; /** * 在这里练习的是线程的中断 * Thread.interrupt()来设置中断状态是true,当一个线程运行时,另一个线程可以调用另一个 * 线程的interrupt()方法来中断他 * Thread.isInterrupt()来获取线程的中断状态 * Thread.interrupted()这是一个静态的方法,用来获取中断状态,并清除中断状态, * 其

Java 线程由浅入深 持续学习

线程状态 在Thread.State 中关于线程状态的解释 NEW Thread state for a thread which has not yet started. RUNNABLE Thread state for a runnable thread. A thread in the runnable state is executing in the Java virtual machine but it may be waiting for other resources from

Java线程池详解(二)

一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉得还有太多内容需要补充,每次都是修修补补,总觉得还缺点什么.在第一篇中,我着重描述了java线程池的原理以及它的实现,主要的点在于它是如何工作的.而本文的内容将更为上层,重点在于如何应用java线程池,算是对第一篇文章的一点补充,这样对于java线程池的学习和总结稍微完整一些. 使用过java线程池

java多线程中synchronize锁的使用和学习,Thread多线程学习(二)

synchronize我的理解是为了保证程序中的原子性和一致性,即当你有两个线程同时操作一段代码的时候,要让这段代码的执行是在任何状态下都是正确的,首先要保证synchronize的使用要对同一个对象和同一把锁使用. [java] view plain copy print? <span style="font-size:14px;">public class TraditionalThreadSynchronized { public static void main(S

Java线程学习经典例子-读写者演示

Java线程学习最经典的例子-读写者,主要用到Thread相关知识如下: -         线程的start与run -         线程的休眠(sleep) -         数据对象加锁(synchronized) -         数据对象的等待与释放(wait and notify) 程序实现: -ObjectData数据类对象,通过synchronized关键字实现加锁,在线程读写者中使用. -ConsumerThread消费者线程,读取数据对象中count值之后,通知生产者

Java 线程池学习

Reference: <创建Java线程池>[1],<Java线程:新特征-线程池>[2], <Java线程池学习>[3],<线程池ThreadPoolExecutor使用简介>[4],<Java5中的线程池实例讲解>[5],<ThreadPoolExecutor使用和思考>[6] [1]中博主自己通过ThreadGroup实现一个线程池(挺方便理解的),使用的是jdk1.4版本,Jdk1.5版本以上提供了现成的线程池. [2]中介绍

黑马程序与----java线程学习

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成.另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源.一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行.由于线程之间的相互制约,致使线程在运行中呈现出间断性.线程也有就绪.阻塞和运