文档章节

Java多线程(二) -- synchronized

兴趣使然的程序员
 兴趣使然的程序员
发布于 2017/05/07 23:33
字数 1375
阅读 22
收藏 1

“同步”是指同一个时间,只有一个线程能对指定的“同步区域”进行访问和修改。

1、使用synchronized实现同步

synchronized修饰的对象有以下几种: 

  • 修饰一个代码块,需要传入被修饰的对象,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; 
  • 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; 
  • 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象; 
  • 修饰一个,需要传入被修饰的类(.class),其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象

前两种实际上是一种“对象锁”,而后两种则是一种“类锁”。

需要注意的是:

  • 对象锁锁死所有的“同步代码块和同步方法”,这意味着如果一个对象有两个同步方法,其中一个在被访问时,另外一个同步方法也会被锁
  • 对象锁只锁“对象”,即两个不同的对象之间互不影响,如果两个线程分别访问两个不同的对象,并不会出现锁
  • 当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。 
  • 类锁用静态方法实现时,同样只锁死所有的静态方法
  • 类锁锁“类”,所有的该类的对象都会受影响
  • “类锁”和“对象锁”不互斥,即一个同步静态方法被访问时,其他线程可以访问同步普通方法和非同步普通方法

修饰代码块:

public  class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        SyncThread syncThread = new SyncThread();
        Thread thread1 = new Thread(syncThread,"SyncThread1");
        Thread thread2 = new Thread(syncThread,"SyncThread2");
        thread1.start();
        thread2.start();
    }
}

class SyncThread implements Runnable{
    private static int count;
    public SyncThread(){count=0;}
    
    @Override
    public void run() {
        //同步代码块
        synchronized (this){
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+":"+(count++));
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public int getCount(){
        return count;
    }
}

修饰方法:

//同步方法,效果和同步代码块相同,但同步代码块更灵活
@Override
public synchronized void run() {
    for (int i = 0; i < 5; i++) {
        System.out.println(Thread.currentThread().getName()+":"+(count++));
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

修饰静态方法:

//同步静态方法
public synchronized static void method1(){
    for (int i = 0; i < 5; i++) {
        System.out.println(Thread.currentThread().getName()+":"+(count++));
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
@Override
public void run() {
    method1();
}

修饰类:

//同步类
@Override
public synchronized void run() {
    synchronized (SyncThread.class){
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+(count++));
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在修饰代码块的用法里,还有一种特殊的用法:

上面提到对象锁会锁死对象里所有的同步代码块,如果想要给分别给不同的块“加不同的锁”,即有两个同步代码块A和B,A每次只能有一个线程访问,B同样同时只能有一个线程访问,但是可以有不同的线程访问A和B。就可以使用下面的方法:为每个需要维护的变量分别创建“维护对象”:

public class Cinema {
    private long vacanciesCinema1;
    private long vacanciesCinema2;
    //下面这两个对象分别用于维护上面这两个变量
    private final Object controlCinema1;
    private final Object controlCinema2;
    //对controlCinema1对象加锁,每次只能有一个线程可以访问sellTickets1方法
    public boolean sellTickets1 (int number){
        synchronized (controlCinema1){
            if (number<vacanciesCinema1){
                vacanciesCinema1-=number;
                return true;
            }else{
                return false;
            }
        }
    }    
    //对controlCinema2对象加锁,同样只有一个线程可以访问sellTickets2方法
    //但是不同的线程可以同时访问这两个方法
    public boolean sellTickets2 (int number){
        synchronized (controlCinema2){
            if (number<vacanciesCinema2){
                vacanciesCinema2-=number;
                return true;
            }else{
                return false;
            }
        }
    }
    
    public Cinema(long vacanciesCinema1, long vacanciesCinema2, Object controlCinema1, Object controlCinema2) {
        this.vacanciesCinema1 = vacanciesCinema1;
        this.vacanciesCinema2 = vacanciesCinema2;
        this.controlCinema1 = controlCinema1;
        this.controlCinema2 = controlCinema2;
    }
}

2、在synchronized代码块中使用wait()、notify()和notifyAll()

wait()、notify()和notifyAll()方法只能在同步代码块中调用,在同步代码块之外调用会抛出异常。三个方法的说明如下:

  • wait():使当前占用同步代码块的线程休眠,并且释放控制这个同步代码块的对象,同时允许其他线程执行这个对象控制的所有同步代码块。调用了wait()的线程会一直等待直到其他线程调用了notify()方法
  • notify():随机唤醒某个调用了wait()的线程,使之可以重新获取对象的控制权
  • notifyAll():唤醒全部调用了wait()的线程

wait()还有一个重载:wait(long)可以规定等待的时长。

使用wait()、notify()进行线程调度:

/**
 * 解决“生产者-消费者”问题
 * 避免队列超过最大大小,生产者无法继续放入对象
 * 避免队列为空时,消费者还要取出对象
 */
public class EventStorage {
    private int maxSize;
    private List<Date> storage;

    public EventStorage() {
        this.maxSize = 10;
        this.storage = new LinkedList<>();
    }

    public synchronized void set(){
        while(storage.size()==maxSize){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        storage.add(new Date());
        System.out.printf("Set: %d",storage.size());
        notifyAll();
    }

    public synchronized void get(){
        while (storage.size()==0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.printf("Get: %d,%s",storage.size(),((LinkedList<?>)storage).poll());
        notifyAll();
    }

    public static void main(String[] args) {
        EventStorage storage = new EventStorage();
        new Thread(new Producer(storage)).start();
        new Thread(new Consumer(storage)).start();
    }
}

class Producer implements Runnable{
    private EventStorage storage;
    public Producer(EventStorage storage){
        this.storage = storage;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.set();
        }
    }
}

class Consumer implements Runnable{
    private EventStorage storage;
    public Consumer(EventStorage storage){
        this.storage = storage;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.get();
        }
    }
}

 

 

© 著作权归作者所有

兴趣使然的程序员
粉丝 23
博文 112
码字总数 87412
作品 0
深圳
程序员
私信 提问
Java多线程学习(二)synchronized关键字(2)

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
2018/04/16
0
0
Java多线程学习(四)等待/通知(wait/notify)机制

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
2018/04/16
0
0
《成神之路-高级篇》Java并发编程——锁

本文是《成神之路系列文章》的第一篇,主要是关于JVM的一些介绍。 持续更新中 数据库相关锁机制 数据库的锁机制 表级锁、行级锁、页级锁 共享锁、排他锁 乐观锁与悲观锁 乐观锁、悲观锁 乐观...

HollisChuang's Blog
2018/10/14
0
0
Java多线程系列之synchronized关键字解析

一、synchronized关键字介绍 synchronized关键字是JVM在软件层面实现的一种独占锁,他依赖于java对象,通过使用它可以把任意一个非空java对象作为锁,如果使用synchronized关键字修饰类中的实...

老韭菜
2018/08/01
0
0
synchronized与ThreadLocal

synchronized是实现java的同步机制。同步机制是为了实现同步多线程对相同资源的并发访问控制。保证多线程之间的通信。 同步的主要目的是保证多线程间的数据共享。同步会带来巨大的性能开销,...

bigYuan
2013/07/18
0
2

没有更多内容

加载失败,请刷新页面

加载更多

rsync工具常用选项以及同步的两种方式

rsync -av /etc/passwd /tmp/1.txt #rsync的本机传输写法 rsync -av /tmp/1.txt 192.168.188.128:/tmp/2.txt #rsync的远程传输rsync格式rsync [OPTION] … SRC ......

林怡丰
今天
3
0
GatewayWorker 报错:stream_socket_server(): unable to connect to tcp://0.0.0.0:1238

GatewayWorker 报错:stream_socket_server(): unable to connect to tcp://0.0.0.0:1238 (Address already in use) 官方文档虽然有相同的问题,但是对我的问题没起作用…… 后面发现自己手贱...

wenzhizhong
昨天
3
0
REST接口

文章来源 https://zhuanlan.zhihu.com/p/28674721?group_id=886181549958119424 http://www.ruanyifeng.com/blog/2014/05/restful_api.html REST 对请求的约定 REST 用来规范应用如何在 HTTP......

Airship
昨天
6
0
Spring Cloud Config 统一配置中心

Spring Cloud Config 统一配置中心 一、统一配置中心 统一管理配置 通常,我们会使用配置文件来管理应用的配置。如一个 Spring Boot 的应用,可以将配置信息放在 application.yml 文件中,如...

非摩尔根
昨天
6
0
android ------ AAPT2 error: check logs for details解决方法

AAPT 是全称是 Android Asset Packaging Tool,它是构建 App,甚至是构建 Android 系统都必不可少的一个工具。它的作用是将所有资源文件压缩打包到Android APK 当中。我们在 Android SDK 目录...

切切歆语
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部