Java多线程 第二章 线程同步synchronized的使用

1.为什么要使用synchronized

在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。
关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。

2.实现原理

synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性

3.synchronized的作用

Synchronized是Java中解决并发问题的一种最常用最简单的方法 ,他可以确保线程互斥的访问同步代码

public class ThreadTest7 {
    public static void main(String[] args){
        Thread11 t = new Thread11();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        t2.start();
    }
}

class Thread11 implements Runnable{
    private static int j = 0;
    public  void run() {
        j++;
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" 你是第" + j + "个线程");

    }
}

输出的结果

Thread-1 你是第2个线程
Thread-0 你是第2个线程

Process finished with exit code 0

两个线程说自己是第2个线程,这个肯定是有问题的,是因为第一线程执行的时候已经把 j 改为了 1,然后休眠了,这个时候第二个线程执行,就在原有的1上再加1,所以后面两个线程输出结果都为2。要怎么解决这个问题呢,这就是线程同步解决的问题,加锁;

public class ThreadTest8 {
    public static void main(String[] args){
        Thread11 t = new Thread11();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        t2.start();
    }
}

class Thread11 implements Runnable{
    private static int j = 0;
    public  void run() {
        synchronized (this){
            j++;
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" 你是第" + j + "个线程");
        }
    }
}

输出结果,添加了synchronized之后,线程1执行完了之后才会去执行线程2。

Thread-0 你是第1个线程
Thread-1 你是第2个线程

Process finished with exit code 0

3.synchronized的四种应用方式

Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

修饰对象作用范围作用对象
一般方法(被称为同步方法)整个方法调用这个方法的对象
静态的方法整个静态方法此类的所有对象
代码块(称为同步代码块)大括号{}括起来的代码调用这个代码块的对象
synchronized后面括号括起来的部分此类的所有对象

举例子:
1、一般方法(被称为同步方法)

public class SynchronTest {
    public static void main(String[] args){
        SycTest s = new SycTest();
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        t1.start();
        t2.start();
    }
}

class SycTest implements Runnable {

    public synchronized void methon1(){
        System.out.println(Thread.currentThread().getName() + " start!");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " end");
    }
    
    public void run() {
        methon1();
    }
}

输出结果,

Thread-0 start!
Thread-0 end
Thread-1 start!
Thread-1 end

Process finished with exit code 0

分析:这里锁加在了方法上,在执行线程0的时候获得的锁,必须等到线程1的方法执行完,线程2方能执行,故线程执行一定是start,end这样的方式

2、静态的方法同步

public class SynchronTest {
    public static void main(String[] args) throws InterruptedException {
        SycTest s = new SycTest();
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        t1.start();
        t2.start();
    }
}

class SycTest implements Runnable {

    private static int count=0;
    
    public synchronized static void method() {
        for (int i = 0; i < 5; i ++) {
            try {
            	count++;
                System.out.println(Thread.currentThread().getName() + ":" + count);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void run() {
        method();
    }
}

我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象,故输出结果如下:

Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9
Thread-1:10

Process finished with exit code 0

3、修饰代码块
3.1、一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞;
3.2、多个线程访问各子的对象即使有synchronized修饰了同步代码块,但是互不阻塞,但是并不能保证静态变量的线程安全性。
3.3、当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。

public class SynchronTest {
    public static void main(String[] args) throws InterruptedException {
        SycTest s = new SycTest();
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        t1.start();
        t2.start();
    }
}

class SycTest implements Runnable {

    private static int count=0;

    public void run() {
   		//this,当前实例对象锁
        synchronized (this){
            for (int i = 0; i < 5; i ++) {
                try {
                    count++;
                    System.out.println(Thread.currentThread().getName() + ":" + count);
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

.一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞
,输出结果如下:

Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9
Thread-1:10

Process finished with exit code 0

4、修饰类

public class SynchronTest {
    public static void main(String[] args) throws InterruptedException {
        SycTest s = new SycTest();
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        t1.start();
        t2.start();
    }
}

class SycTest implements Runnable {

    private static int count=0;

    public void run() {
    	//class对象锁
        synchronized (SycTest.class){
            for (int i = 0; i < 5; i ++) {
                try {
                    count++;
                    System.out.println(Thread.currentThread().getName() + ":" + count);
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

总结:
1、 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
2、 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
3、实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值