博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程(二) -- synchronized
阅读量:7097 次
发布时间:2019-06-28

本文共 5089 字,大约阅读时间需要 16 分钟。

hot3.png

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

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;    }}

修饰方法:

//同步方法,效果和同步代码块相同,但同步代码块更灵活@Overridepublic 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();        }    }}@Overridepublic void run() {    method1();}

修饰类:

//同步类@Overridepublic 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

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
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(); } }}

 

 

转载于:https://my.oschina.net/pierrecai/blog/894499

你可能感兴趣的文章
83. Remove Duplicates from Sorted List(从有序链表中删除重复节点)
查看>>
原型设计工具
查看>>
Python之进程
查看>>
linux下按键驱动程序
查看>>
excel读取csv的字符集问题
查看>>
论坛迁移日记——discuz X2.5 迁移详细教程
查看>>
vs2010中的ADO控件及绑定控件
查看>>
GPS坐标转百度地图并且加载地图示例.支持微信端访问
查看>>
nginx 配置白名单
查看>>
iOS开发助手、ipa上传工具、苹果APP快速上架辅助工具Appuploader
查看>>
Rstudio编辑界面美化设置
查看>>
重写对象ToString方法
查看>>
备忘: C++中的 vector 容器
查看>>
smt中查看图片与视频缩略图中,如何获得小视频的长度。
查看>>
图片(img标签)的onerror事件
查看>>
2013应届毕业生“百度”校招应聘总结
查看>>
CentOS系统启动流程
查看>>
SQLite数据库_实现简单的增删改查
查看>>
批量查询 xml的方式 还一种是表变量
查看>>
Java7/8 中 HashMap 和 ConcurrentHashMap的对比和分析
查看>>