继承Runnable 实现Synchronized 同步锁

 

在java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,下面看看这个关键字的用法。

因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识。

java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。

java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的

 

若一个非抽象类实现一个接口,就必须重写接口中所有的方法,所以你实现Runnable接口时,会重写run()方法,run()方法只是把该线程编程可就绪状态,start()方法是将该线程变为可运行状态,好好理解下。。。

 

误区一:synchronized关键字只能用在实现Runnable或者继承了Thread类的子类的方法里面。

正解:如果有一块代码(或方法)可能被多个线程同时访问,然后里面操作的数据修改操作可能因为不同线程的操作而不一致的时候,使用synchronized锁定这块代码,确保同时只有一个线程访问这个代码块。也就是说,关键字synchronized可以用在任何类的方法里面,即使该类没有实现Runnable接口或者继承Thread类。

 

误区二:synchronized(this)和synchronized(object)作用范围完全不同。

正解:当多个线程访问同一个类 A 的方法 A() 的时候。并且,这个方法 A() ,要求一个线程执行完了之后再给另外一个线程去执行。那么,这个方法 A() 就必须加上 synchronized 关键字,或者,在该方法 A() 中写上 

synchronized(this//指代当前类A的实例) { }

如果不在声明方法 A() 时,加上 synchronized 关键字,或者,不在方法 A() 中加上 synchronized(this){ }
同步块的时候可以在线程类的 run() 方法内

synchronized(Object //指代类A的实例){
 Object.A();
}
实现多线程同时有序访问该同步块内类 A 的方法 A() 的目的。

 

object本身就包含this的情况。

this 指代的是当前同步块所在方法所在的类,当不需要引用别的类的时候。

object 指代的是需要调用的类,引用了别的类,且需要处理多线程并发访问时,object 指代的是被引用的类。如果没有引用别的类,则指代的就是同步块所在方法所在的类本身

 

一般来说,一个方法处理的内容很多,如果synchronized修饰以后,其他同步方法就必须等待其执行完毕才可以继续执行,如果该方法需要较长时间处理,这就明显会降低效率,失去了多线程的意义,所以我们可以考虑将同步的范围缩小,即从同步一个方法缩小为同步一段代码块,这就是同步代码块产生的原因。同步代码块的语法是:

 

synchronized (this) {}
synchronized (object) {}

synchronized(this)我称之为this同步代码块,针对的是当前对象;synchronized(object)我称之为非this同步代码块,针对的是object对象。

记住一点即可:不论是同步方法还是同步代码块,实质上都是争夺锁的问题,而锁一定是对象级的,即一个对象只会产生一个锁,所以只要有一个线程在执行synchronized修饰的东西(不论是方法还是代码块),那么其他线程都无法访问被synchronized修饰的方法或代码块。
但是注意使用非this同步代码块的时候,里面的object不要用String类型的,因为大家都知道JVM具有String常量池缓存的功能,所以使用String类型可能产生问题。

 

下面用一个栗子来区分同步方法还有同步代码块的synchronized (this)和synchronized (object)

MethodSync 方法

例如 :

public class MethodSync{
    private String anyString;

    public MultiSyn(String anyString) {
        this.anyString = anyString;
    }

/*
* 同步类方法
* @Task : 测试 synchronized 修饰方法时锁定的是调用该方法的对象
* @param name 线程的标记名称
*/
public synchronized void method(String name){
System.out.println(name + " Start a sync method");
try{
Thread.sleep(300);
}catch(InterruptedException e){}
System.out.println(name + " End the sync method");
}

/**
* 同步代码块
* @param name
*/
public void asyncMethod(String name){
synchronized (MethodSync.class){
try {
System.out.println("同步代码块!!!!"+name);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(name + " End the sync method");
}
}
}

/** * 非this同步代码块 */ public void synObject() { synchronized (anyString) { System.out.println(anyString + " IN synchronized (anyString)同步代码块"); System.out.println(anyString + " OUT synchronized (anyString)同步代码块"); } } }

 RunnableService 调用

public class RunnableService implements Runnable {


    /**
     *  一、
     * test1 先于 test2 执行 同步方法,但是却后于 test2 结束。这里并没有达到互斥的效果!
     * 原因是:MethodSync是实例变量,每次创建一个Test对象就会创建一个MethodSync对象,
     * synchronized 只会锁定调用method()方法的那个MethodSync对象,
     * 而这里创建的两个线程分别拥有两个不同的MethodSync对象,它们调用method方法时就没有互斥关系。
     *
     */

    /**
     * 二、
     * 当把Test.java 中的MethodSync 变量 用 static 来修饰时,执行结果如下:
     * 这里,正确实现了同步作用。原因如下:这里也创建了二个线程(Test 对象),
     * 但是每个Test对象共享MethodSync 变量,也即只有一个MethodSync 变量在两个线程中执行 method方法,
     * 这样两个线程在执行到method 方法这段代码时就会形成互斥
     *
     */

    private String name;

    private static MethodSync methodSync = new MethodSync();
//    private MethodSync methodSync = new MethodSync();

    public RunnableService(String name){
        this.name = name;
    }

    /**
     * run方法它本身启动不了多线程。多线程在实现的时候看着只是重写run方法
     * ,调用start方法启动,其实start方法里边还有其它操作:创建线程,调用run方法。
     */
    @Override
    public void run() {
        methodSync.method(name);
    }

    public static void main(String[] args) {
//         MethodSync methodSync = new MethodSync();
//        methodSync.method("你好1");
//        methodSync.method("你好2");
        Thread t1 = new Thread(new RunnableService("test 1"));
        Thread t2 = new Thread(new RunnableService("test 2"));
        t1.start();
        t2.start();

    }

}

 

  打印日志

test 1 Start a sync method
test 1 End the sync method
test 2 Start a sync method
test 2 End the sync method

Process finished with exit code 0

 

代码 : https://gitee.com/xdymemory00/AsyncWithLock.git

 

posted @ 2017-11-30 11:28  Mr.DongYang  阅读(2059)  评论(0编辑  收藏  举报