• java中synchronize锁 volatile thread.join()方法的使用


    对于并发工作,你永远不知道一个线程何时运行,你需要某种方式来避免两个任务访问相同的资源,即要避免资源竞争,至少在关键代码上不能出现这样的情况,否则多个线程同时对某个内存区域操作会导致数据破坏。

    程序代码中的临界区是需要互斥访问的,同一时刻只能有一个线程来访问临界区,也就是线程对临界区的访问时互斥的。

    竞争条件:当多个线程同时访问某个共享的内存区域并且对其进行读写操作时,就会出现数据破坏。这就是竞争条件。避免竞争条件的方法是synchronized加锁。

    样例,设有一个现成,该线程的任务是对共享变量count值+1。设count值的初始值为0,开辟1000个相同的线程,那么线程执行完后,count的值应该为1000(因为每个线程中都对count做了加一操作),但是实际效果会出现错误,这是因为出现了竞争条件。

    解决办法:使用synchronized锁,

    先定义一个object对象:

    private final static Object lockObj = new Object();

    下面即是同步块,在同步块中进行关键操作。

    					synchronized(lockObj)
    					{
    						syn.count = syn.count+1;	
    						temp = temp+1;
    					}
    

      示例代码:

    package ex5_Synchronize;
    
    public class SynchronizeTest {
        
        //volatile方法可以保证每次都会去内存中读取变量的值,即遵守happen-befor原则,但是骑兵不能保证并发时数据不被破坏
        public volatile static int temp = 0;
        
        private final static Object lockObj = new Object();
        
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            
            
            Thread threads[] = new Thread[1000];
            
            for(int i=0;i<1000;i++)
            {
                threads[i] = new Thread(new Runnable() {
                    
                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        //使用synchronize上锁,保证线程互斥访问
    
                        synchronized(lockObj)
                        {
                            syn.count = syn.count+1;    
                            temp = temp+1;
                        }
                    }
                });
                threads[i].start();    
            }
            
            //调用join,让主线程等待当前线程执行完毕后再执行
            for(int i = 0;i<1000;i++)
            {
                try {
                    threads[i].join();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            
            //如果没有thread.join,那么其他线程还没执行完毕,主线程就要结束了,此时print输出的值并不是所有线程执行完后的值,所以不能说明问题
            System.out.print("count的值:"+syn.count+"
    ");
            System.out.print("temp的值:"+temp);                
        }
    }

    输出结果

    count的值:1000
    temp的值:1000

    -------------------------------------------------------------------------------

    下面是正题

    JAVA中synchronized关键字能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。假如再细的分类,synchronized可作用于instance变量、object reference(对象引用,例如this)、static函数和class literals(类名称字面常量)身上。下面讨论synchronized用到不同地方对代码产生的影响:
    假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都能够调用他们。
    1.把synchronized当作函数修饰符时,示例代码如下:
    public synchronized void method(){
    //….
    }
    这也就是同步方法,那这时synchronized锁定的是哪个对象呢?他锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,他们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却能够任意调用这个被加了synchronized关键字的方法。上边的示例代码等同于如下代码:
    public void method()
    {
    synchronized (this) // (1)
    {
    //…..
    }
    }
    (1)处的this指的是什么呢?他指的就是调用这个方法的对象,如P1。可见,同步方法实质是将synchronized作用于Object Reference。那个拿到了P1对象锁的线程,才能够调用P1的同步方法,而对P2而言,P1这个锁和他毫不相干,程式也可能在这种情形下摆脱同步机制的控制,造成数据混乱。
    2.同步块,示例代码如下:
    public void method(SomeObject so) {
    synchronized(so)
    {
    //…..
    }
    }
    这时,锁就是so这个对象,谁拿到这个锁谁就能够运行他所控制的那段代码。当有一个明确的对象作为锁时,就能够这样写程式,但当没有明确的对象作为锁,只是想让一段代码同步时,能够创建一个特别的instance变量(它得是个对象)来充当锁:
    class Foo implements Runnable
    {
    private byte[] lock = new byte[0]; // 特别的instance变量
    Public void method()
    {
    synchronized(lock) { //… }
    }
    //…..
    }
    注:零长度的byte数组对象创建起来将比任何对象都经济。查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
    3.将synchronized作用于static 函数,示例代码如下:
    Class Foo
    {
    public synchronized static void method1() // 同步的static 函数
    {
    //….
    }
    public void method2()
    {
    synchronized(Foo.class) // class literal(类名称字面常量)
    }
    }
    代码中的method2()方法是把class literal作为锁的情况,他和同步的static函数产生的效果是相同的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class类,而不再是由这个Class产生的某个具体对象了)。

    转载请注明:struts教程网 » synchronized的深刻认识

    如何来定义共享变量:

    1、在主类中定义一个final变量,让其在其他线程中也可以执行。

    2、使用static定义线程公用的成员变量,在一个类实现runnable接口,其中定义一个static变量,这个变量就为所有使用此接口的类对象所共用,即所有的类对象共用一份这个变量的拷贝。

    1、java中synchronize可以用来给对象、代码块等加锁。synchronize关键字可以出现在函数外,也可以出现在函数体

    2、没有volatile就没有java多线程

    3、Thread.join()可以设置让当前线程执行完毕后再执行其他线程,这在控制线程的执行顺序上很有用。

    下面通过一个代码来描述java多线程中可能出现的资源竞争条件(race condition)问题。

  • 相关阅读:
    0514JS基础:操作document对象、事件、this
    Java 多态
    Java 抽象类和接口
    Vue中v-for属性
    Vue基础语法
    Vue 自定义按键修饰符,自定义指令,自定义过滤器
    ES6 剩余参数
    ES6 箭头函数
    ES6 解构赋值
    ES6 变量声明 var let const的区别
  • 原文地址:https://www.cnblogs.com/wll-zju/p/4331092.html
Copyright © 2020-2023  润新知