Java多线程编程学习笔记 synchronized的理解 原子操作 actomic compareAndSet

Java多线程编程学习笔记  原子操作

原子概念

原子,是一种很小的粒子,可以理解,不是成功就是失败

关键字:synchronized、Atomic系列、compareAndSet、volatile 

 
1. 丌会被线程调度机制打断的操作,对于其它线程而言,看不到中间态,非成功即失败,
  比如int a = 1是原子操作 
2. long b = 1L不一定是原子操作,跟系统位数相关,double同理; 

3. i++不是原子操


对于synchronized(this) 的理解可以看博客:点击打开链接

1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

/**
 * 版权所有 (c) 2016, 
 */
package helloTest.Thread.test;

/**
 * 类说明:
 * 
 * 
 * Modify Information:
 * Author        Date          Description
 * ============ =========== ============================
 * 2016-7-12    Create this file
 * 
* */ public class Thread1 implements Runnable{ /** * 一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。 * 另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块 * 两个线程要同时访问同一个synchronized 代码块的时候,有先后顺序,执行完一个,才会执行第二个 * @param args */ 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(); } @Override public void run() { synchronized(this){ for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+" synchronized loop "+i); } } } }
运行结果:
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


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

/**
 * 版权所有 (c) 2016,  
 */
package helloTest.Thread.test;

/**
 * 类说明:
 * 
 * 
 * Modify Information:
 * Author        Date          Description
 * ============ =========== ============================
 * 2016-7-12    Create this file
 * 
* */ public class Thread2 implements Runnable { /** * 然而,当一个线程访问object的一个synchronized(this)同步代码块时, * 另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。 * @param args */ 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(); } /* * 下面两个方法是Thread2线程的, 两个线程, 一个线程方法synchronized(), 另外一个线程访问非synchronized() 允许访问 */ public void m4t1(){ synchronized(this){ int i=5; while(i-->0){ System.out.println(Thread.currentThread().getName()+" : "+i); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } public void m4t2(){ int i=5; while(i-->0){ System.out.println(Thread.currentThread().getName()+" : "+i); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { } }
结果:

t1 : 4
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
t1 : 3
t1 : 2
t1 : 1
t1 : 0

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

/**
 * 版权所有 (c) 2016,
 */
package helloTest.Thread.test;

/**
 * 类说明:
 * 
 * 
 * Modify Information:
 * Author        Date          Description
 * ============ =========== ============================
 * 2016-7-12    Create this file
 * 
* */ public class Thread3 { /** * 三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时, * 其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。 * @param args */ public static void main(String[] args) { final Thread3 myt2=new Thread3(); Thread t1=new Thread( new Runnable(){ public void run(){ myt2.m4t1(); } },"Thread3_t1" ); Thread t2=new Thread( new Runnable(){ public void run(){ myt2.m4t2(); } },"Thread3_t2" ); t1.start(); t2.start(); } /* * 两个线程当同时访问Thread3 的两个不同的synchronized()代码时,和两个线程访问同一个synchronized效果时一致的, 一个先执行完然后再执行下一个 */ public void m4t1(){ synchronized(this){ int i=5; while(i-->0){ System.out.println(Thread.currentThread().getName()+" : "+i); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } public void m4t2(){ synchronized(this){ int i=5; while(i-->0){ System.out.println(Thread.currentThread().getName()+" : "+i); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }

运行结果:

Thread3_t1 : 4
Thread3_t1 : 3
Thread3_t1 : 2
Thread3_t1 : 1
Thread3_t1 : 0
Thread3_t2 : 4
Thread3_t2 : 3
Thread3_t2 : 2
Thread3_t2 : 1
Thread3_t2 : 0


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

/**
 * 版权所有 (c) 2016,  
 */
package helloTest.Thread.test;

/**
 * 类说明:
 * 
 * 
 * Modify Information:
 * Author        Date          Description
 * ============ =========== ============================
 * 2016-7-12    Create this file
 * 
* */ public class Thread4 { /** * 四、第三个例子同样适用其它同步代码块。 * 也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。 * 结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。 * @param args */ public static void main(String[] args) { final Thread4 myt4=new Thread4(); Thread t1=new Thread( new Runnable(){ public void run(){ myt4.m4t1(); } },"Thread4_t1" ); Thread t2=new Thread( new Runnable(){ public void run(){ myt4.m4t2(); } },"Thread4_t2" ); t1.start(); t2.start(); } /* * 两个线程访问两个不同的synchronized(this) 这个this就是当前对象,在这里就是Thread4,不管时synchronized是什么, 执行结果是有顺序的 */ public void m4t1(){ synchronized(this){ int i=5; while(i-->0){ System.out.println(Thread.currentThread().getName()+" : "+i); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void m4t2(){ int i=5; while(i-->0){ System.out.println(Thread.currentThread().getName()+" : "+i); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }

运行结果:

Thread4_t2 : 4
Thread4_t2 : 3
Thread4_t2 : 2
Thread4_t2 : 1
Thread4_t2 : 0
Thread4_t1 : 4
Thread4_t1 : 3
Thread4_t1 : 2
Thread4_t1 : 1
Thread4_t1 : 0

5、以上规则对其他对象锁同样适用

6、两个线程访问一个方法,方法中有synchronized 还有非synchronized 的 执行顺序, 结果很可能,A线程在执行非synchronized B线程在执行synchronized代码

/**
 * 版权所有 (c) 2016,中金支付有限公司  
 */
package helloTest.Thread.test;

/**
 * 类说明:
 * 
 * 
 * Modify Information:
 * Author        Date          Description
 * ============ =========== ============================
 * 2016-7-12    Create this file
 * 
* */ public class Thread5 implements Runnable{ public static void main(String[] args) { // TODO Auto-generated method stub /* * 如果两个方法访问同一个方法, 这个方法中有synhronized 还有一个非synchronized 块,执行顺序?? * */ Thread5 t5=new Thread5(); Thread ta=new Thread(t5,"A"); Thread tb=new Thread(t5,"B"); ta.start(); tb.start(); } @Override public void run() { // TODO Auto-generated method stub synchronized(this){ for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+" synchronized loop "+i); } } for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+" not ynchronized loop "+i); } } }

运行结果:也就是说有线程交叉的

A synchronized loop 0
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
A not ynchronized loop 0
B synchronized loop 0
A not ynchronized loop 1
B synchronized loop 1
A not ynchronized loop 2
B synchronized loop 2
A not ynchronized loop 3
B synchronized loop 3
A not ynchronized loop 4
B synchronized loop 4
B not ynchronized loop 0
B not ynchronized loop 1
B not ynchronized loop 2
B not ynchronized loop 3
B not ynchronized loop 4


理解原子操作:
package helloTest.Thread;


import cpcn.payment.tool.middleware.log.LogType;
import cpcn.payment.tool.middleware.log.Loggerx;

public abstract class GroupBase implements Runnable {

    public static final Loggerx logger = Loggerx.getLogger("system");

    private boolean running = false;

    protected String[] arguments;

    /*
     * (non-Javadoc)
     * @see java.lang.Runnable#run()
     * 对于下列代码的理解,为什么加synchronized, 有很多个JobGroup 继承GroupBase 
     *  也就是说有多个线程访问 GroupBase 对象这个Object 根据synchronized原理,为了避免多个线程同时串改running 这个变量
     *  需要将running 赋值 过程加速,保证只运行有一个JobGroup 可以更改running的值, 在一定意义上形成了互斥
     *  同时为什么加一个本地局部变量localRunning,这个变量,各个线程都是独有一份的,
     *  比如A, B线程启动了:
     *  A线程先取得object锁  synchronized (this) {
            localrunning = running;
            if (!running) {
                running = true;
            }
        }执行完这段,就立即史昂锁了,B 线程就立即执行 synchronized (this) {
            localrunning = running;
            if (!running) {
                running = true;
            }
        }也就是说A线程 还没来得及执行完下面那段: 
         if (localrunning) {
            logger.warn(LogType.EX, "jobGroup" + arguments[0] + " is busy.");
        } else {
            doTask();
            synchronized (this) {
                running = false;
            }
        }
        B线程如果执行的块,如果没有localrunning 的判断,很可能在A线程修改running 之前B线程就已经修改runnning了
        
        
        
        GroupBase jobGroup = (GroupBase) SystemEnvironment.applicationContext.getBean(beanName);
            if (null != jobGroup) {
                
                new Thread(jobGroup).start();
            } else {
                logger.error(LogType.INFO, "There is no JobGroup named " + beanName);
            }
     */
    public void run() {
        boolean localrunning = false;
        synchronized (this) {
            localrunning = running;
            if (!running) {
                running = true;
            }
        }
        if (localrunning) {
            logger.warn(LogType.EX, "jobGroup" + arguments[0] + " is busy.");
        } else {
            doTask();
            synchronized (this) {
                running = false;
            }
        }
    }

    public abstract void doTask();

    public synchronized boolean getRunning() {
        return running;
    }

    public void setArguments(String[] arguments) {
        this.arguments = arguments;
    }
}

上面的代码就是为了多个线程同时访问时,只允许一个线程来修改running 这个类变量


考虑如何使用原子操作来保证这一点尼?


package helloTest.Thread;


import java.util.concurrent.atomic.AtomicBoolean;

import cpcn.payment.tool.middleware.log.LogType;
import cpcn.payment.tool.middleware.log.Loggerx;

public abstract class GroupBase implements Runnable {

    public static final Loggerx logger = Loggerx.getLogger("system");
    
    // 原子操作的方式进行实现
    private AtomicBoolean running = new AtomicBoolean(false);

    protected String[] arguments;

    public void run() {
        
        /*
         * 原子操作的方式进行实现
         */
        if(running.compareAndSet(false, true)){
            /*
             * 意思时如果running 从false 改成了true  成功了,说明有一个线程已经启动了并且成功获取object锁
             */
            doTask();
            running.compareAndSet(true, false);
        }else{
            logger.warn(LogType.EX, "jobGroup" + arguments[0] + " is busy.");
        }
        
    }

    public abstract void doTask();

    public void setArguments(String[] arguments) {
        this.arguments = arguments;
    }
}


应用场景:

1. 除了例子中公共因子只有一个的简单场景,否则别轻易使用Atomic

 2. 在丌严格要求性能的情况下,优先使用synchronized






你可能感兴趣的:(Java多线程编程学习笔记 synchronized的理解 原子操作 actomic compareAndSet)