Java多线程及synchronized的运用代码演示加详细注释

对某个对象加锁

/**

  • synchronized关键字
  • 对某个对象加锁
    */
    public class T {
    private int count = 10;
    private Object o = new Object();
    public void m(){
    //任何线程要执行下面代码,必须先拿到o锁
    synchronized (o){
    count–;
    System.out.println(Thread.currentThread().getName() + " count = " + count);
    }
    }
    }

对当前对象加锁(一)

/**

  • synchronized关键字
  • 对某个对象加锁
    */
    public class T {
    private int count = 10;
    public void m(){
    //任何线程要执行下面的代码,必须先拿到this的锁
    synchronized (this){
    count–;
    System.out.println(Thread.currentThread().getName() + " count = " + count);
    }
    }
    }

对当前对象加锁(二)

/**

  • synchronized关键字
  • 对某个对象加锁
    */
    public class T {
    private int count = 10;
    //同等于在方法的代码执行要synchronized(this)
    public synchronized void m(){
    count–;
    System.out.println(Thread.currentThread().getName() + " count = " + count);
    }
    }

对当前对象加锁(三)

/**

  • synchronized关键字
  • 对某个对象加锁
    */
    public class T {
    private static int count = 10;
    //这里等同于synchronized(xxx.T.class)
    public synchronized static void m(){
    count–;
    System.out.println(Thread.currentThread().getName() + " count = " + count);
    }
    public static void ms(){
    synchronized (T.class){
    count–;
    }
    }
    }

synchronized使用案例

/**

  • 实验结果:在run方法如果不加上synchronized,会产生线程抢占,

  • 如果加上就确保了线程运行的原子性,先执行的执行,后执行的等待。
    */
    public class T implements Runnable {

    private int count = 10;

    @Override
    public /synchronized/ void run() {
    count–;
    System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

    public static void main(String[] args) {
    T t = new T();
    for (int i = 0; i < 5; i++){
    new Thread(t, “Thread” + i).start();
    }
    }
    }

在一个对象一面两个方法加不加synchronized修饰的影响

/**

  • 实验结果:此案例说明加synchronized修饰后的线程对象,

  • 和不加synchronized修饰,两者同时运行并不冲突。
    */
    public class T {
    public synchronized void m1(){
    System.out.println(Thread.currentThread().getName() + " m1 start…");
    try {
    Thread.sleep(10000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + " m1 end…");
    }

    public void m2(){
    try {
    Thread.sleep(5000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + " m2…");
    }

    public static void main(String[] args) {
    T t = new T();
    //语法有点叼
    new Thread(()->t.m1(),“t1”).start();
    new Thread(()->t.m2(),“t2”).start();

     //和上面结果一样
     //new Thread(t::m1, "t1").start();
     //new Thread(t::m2, "t2").start();
    

    }
    }

脏读以及解决方法

/**

  • 脏读

  • 实验结果:实验结果为0.0。

  • 解决方法:确保读写都加上synchronized,保证原子性。
    */
    public class Account {
    String name;
    double balance;

    public synchronized void set(String name, double balance){
    this.name = name;
    //这里加上间隔时间的意义在于模拟线程被其他线程进行调用
    try {
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    this.balance = balance;
    }

    public /synchronized/ double getBalance(String name){
    return this.balance;
    }

    public static void main(String[] args) {
    Account account = new Account();
    new Thread(()->account.set(“xipiker”, 21.0)).start();

     try {
         TimeUnit.SECONDS.sleep(1);
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
    
     System.out.println(account.getBalance("xipiker"));
    

    }
    }

synchronized获得的锁可重入

/**

  • 一个同步方法可以调用另一个同步方法,一个线程已经拥有某个对象的锁,

  • 再次申请的时候仍然会得到该兑现的锁。也就是说synchronized获得的

  • 锁是可重入的。
    */
    public class T {
    synchronized void m1(){
    System.out.println(“m1 start…”);
    try {
    TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    m2();
    }

    synchronized void m2(){
    try {
    TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(“m2 start…”);
    }

    public static void main(String[] args) {
    T t = new T();
    new Thread(()->t.m1(), “t1”).start();
    }
    }

子类调用父类的同步方法

/**

  • 子类调用父类的同步方法
    */
    public class T {
    synchronized void m(){
    System.out.println(“m start”);
    try {
    TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(“m end”);
    }

    public static void main(String[] args) {
    new TT().m();
    }
    }

class TT extends T{
synchronized void m(){
System.out.println(“child m start”);
super.m();
System.out.println(“child m end”);
}
}

程序运行出现异常,默认情况锁会被释放

/**

  • 程序在执行过程中,如果出现异常,默认情况下锁会被释放。
  • 所以,在并发处理过程中,有异常要多加小心,不然可能会发生不一致情况。
  • 比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,
  • 这时候如果异常处理不合适,在第一个线程中抛出异常,其他线程就会进入同步代码区,
  • 有可能会访问到异常产生时的数据。因此要非常小心的处理同步业务逻辑中的异常。
  • 实验结果:锁被释放了,原子性不存在了,第二个线程接着第一个线程继续执行。
    */

public class T {
int count = 0;
synchronized void m(){
System.out.println(Thread.currentThread().getName() + " start…");
while (true){
count++;
System.out.println(Thread.currentThread().getName() + " count = " + count);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//这里我们模拟异常
//此处抛出异常,锁将释放,要想不被释放,可以在这里进行catch,然后让循环继续
if (count == 5){
int i = 1/0;
}
}
}
public static void main(String[] args) {
T t = new T();
Runnable r = new Runnable() {
@Override
public void run() {
t.m();
}
};

    new Thread(r, "t1").start();

    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    new Thread(r, "t2").start();
}
}

volatile使一个变量在多个线程间可见

/**

  • volatile关键字,使一个变量在多个线程间可见。

  • 例子: A B线程都用到一个变量,java默认是A线程中保留一份copy,这样如果B线程修改了该变量,则A线程未必知道

  • 使用volatile关键字,会让所有线程都会读到变量的赋值。

  • 在下面代码中running是存在于堆内存的t对象中,当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,

  • 在运行过程中直接使用这个copy,并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行。

  • 使用volatile,将会强制所有线程都去堆内存中读取running的值。

  • volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized。
    */
    public class T {
    volatile boolean running = true;
    void m(){
    System.out.println(“m start…”);
    while (running){

     }
     System.out.println("m end!");
    

    }

    public static void main(String[] args) {
    T t = new T();
    new Thread(t::m, “t1”).start();

     try {
         TimeUnit.SECONDS.sleep(1);
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
    
     t.running = false;
    

    }
    }

volatile并不能保证原子性

/**

  • volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能代替synchronized。

  • 实验结果:多个线程间比如A线程执行中的count为100,B线程计算的A的count在加一为101,然后轮到A执行时他的count值时应该count结果为102,

  • 但是执行了为101,所以原本count的102值又被清回101。
    */
    public class T {
    volatile int count = 5;
    void m(){
    for(int i = 0; i < 1000; i++)
    count++;
    }

    public static void main(String[] args) {
    T t = new T();
    List threads = new ArrayList();

     for (int i = 0; i < 10; i++){
         threads.add(new Thread(t::m, "Thread-" + i));
     }
    
     threads.forEach((o)->o.start());
    
     threads.forEach((o)->{
         try {
             o.join();
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     });
     System.out.println(t.count);
    

    }
    }

使用AtomXXX类,确保变量的原子性

/**

  • 确保变量的原子性,更加高效的方法可以使用AtomXXX类。

  • AtomXXX类本身方法都是原子性,但不能保证多个方法连续调用是原子性。
    */
    public class T {
    AtomicInteger count = new AtomicInteger(0);
    void m(){
    for (int i = 0; i < 10000; i++)
    //if count.get() < 1000 ->这里如果执行那么就不在具有原子性了,
    // 因为可能会有线程执行到count.get() < 1000里面去,而又有的线程执行了下面的++操作。
    count.incrementAndGet(); //count++
    }

    public static void main(String[] args) {
    T t = new T();
    List threadList = new ArrayList();
    for (int i = 0; i < 10; i++){
    threadList.add(new Thread(t::m, “thread-” + i));
    }

     threadList.forEach((o)->o.start());
    
     threadList.forEach((o)->{
         try {
             o.join();
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     });
     System.out.println(t.count);
     }
     }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值