Java04 线程同步问题解决——线程锁(同步锁互斥锁)

Posted 滚滚向前滚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java04 线程同步问题解决——线程锁(同步锁互斥锁)相关的知识,希望对你有一定的参考价值。

目录

[TOC]

写在最前:

可能有误,请大家批评指正

一、线程切换

Java中,如果要实现在一个线程间的线程切换,需要在线程中使用Thread.yield()即可让出CPU时间。

二、线程锁(也叫同步锁、互斥锁)

线程锁可以在有效缩小同步范围的同时,尽可能的保证并发效率

2.1 使用synchronized关键字对方法进行加锁

对整个线程处理加锁(严重影响效率,不常用

2.1.1 语法

public synchronized void test(){
    
}

2.1.2 案例

package com.javase.thread;

import javax.management.RuntimeErrorException;

/**
 *      这个类主要讲了Sychronized关键字,给方法加了Synchronized关键字以后,线程在调用这个方法时,相当于对这个方法加了锁,
 * 那么其他线程就不能调用这个方法了(处于阻塞状态)
 * 
 * @author gupan
 *
 */
public class ThreadSyncSychronized {
    public static void main(String[] args) {
        final Table2 table = new Table2();
        Thread t1 = new Thread() {
            public void run() {
                while (true) {
                    try {
                        int bean = table.getBean();
                        Thread.yield(); // 线程切换语句,让出CPU时间
                        System.out.println(getName() + ","  + table.getBean());
                    } catch (RuntimeErrorException e) {
                        System.out.println(getName() + ","  + e);
                        break;
                    }
                    
                }
            }
        };
        
        Thread t2 = new Thread() {
            public void run() {
                while (true) {
                    try {
                        int bean = table.getBean();
                        Thread.yield(); // 线程切换语句,让出CPU时间
                        System.out.println(getName() + ","  + table.getBean());
                    } catch (RuntimeErrorException e) {
                        System.out.println(getName() + "," + e);
                        break;
                    }
                }
            }
        };
        t2.start();
        t1.start();
    }
}

class Table2{
    // 桌子上有20元钱
    private int beans = 20;
    
    public synchronized int getBean() throws RuntimeErrorException{
        if (this.beans == 1) {
            throw new RuntimeErrorException(null, "地主家没有余粮了");
        }
        Thread.yield(); // 线程切换语句,让出CPU时间
        return this.beans--;
    }
}

2.2 使用synchronize关键字对线程方法中的某一部分加锁(同步块的方式)

2.2.1 语法

// 注意这里是this,可以写new Object(),但是这样起不到加锁的效果
// 也就是说,要实现加锁的效果,需要保证是对同一个对象(也就是保证synchronized后面所跟对象是同一个)加锁
synchronized(this){
    ···
    // 加锁语句
}

2.2.2 案例

package com.javase.thread;
/**
 * 这个类主要演示小范围的使用锁。尽可能的提高并发效率
 * 
 * synchronized(this){
 *      ···
 *      // 加锁语句
 * }
 * 
 * @author Think
 *
 */
public class ThreadSyncLock {
    public static void main(String[] args) {
        final Shop shop = new Shop();
        Thread t1 = new Thread() {
            public void run() {
                shop.buy();
            }
        };
        
        Thread t2 = new Thread() {
            public void run() {
                shop.buy();
            }
        };
        
        t1.start();
        t2.start();
    }
}

class Shop{
    public void buy() {
        Thread t = Thread.currentThread();
        try {
            System.out.println(t.getName() + "正在挑衣服");
            Thread.sleep(1000);
            
            // 需要传入当前方法所属对象,所以这里要传入this
            synchronized (this) {
                System.out.println(t.getName() + "正在试衣服");
                Thread.sleep(1000);
            }
            
            System.out.println(t.getName() + "结账离开");
            Thread.sleep(1000);
        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

2.3 静态方法加锁

如果对静态方法加了synchronized关键字,由于静态方法只有一份,整个方法一定是加了互斥锁

package com.javase.thread;

import com.javase.string.Object;

/**
 * 静态方法的同步
 *      当一个静态方法被synchronized修饰以后,那么该方法就是同步方法,由于静态方法从属类,
 * 全局就一份,所以同步的静态方法一定具有同步效果,与对象无关
 * 
 * @author gupan
 *
 */
public class ThreadSyncStatic { 
    public static void main(String[] args) {
        Thread t1 = new Thread() {
            public void run() {
                Foo.dosome();
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                Foo.dosome();
            }
        };
        t1.start();
        t2.start();
    }
}


class Foo{
    public static synchronized void dosome() {
        try {
            Thread t = Thread.currentThread();
            System.out.println(t.getName() + "正在等待运行dosome方法");
            Thread.sleep(1000);
            
            System.out.println(t.getName() + "正在运行dosome方法");
            Thread.sleep(1000);
            
            System.out.println(t.getName() + "执行dosome方法完毕");
        }catch (Exception e) {
            // TODO: handle exception
        }
    }
}

运行结果:

Thread-1正在等待运行dosome方法
Thread-1正在运行dosome方法
Thread-1执行dosome方法完毕
Thread-0正在等待运行dosome方法
Thread-0正在运行dosome方法
Thread-0执行dosome方法完毕

2.3 互斥锁

2.3.1 同步锁和互斥锁

同步锁和互斥锁原理相同,存在的是用法上的小差异。当两个线程调用同一段代码,并且,对于两个线程的同步监视器,看到的代码相同,那就是同步锁;但是,对于几段代码,用一个同步监视器进行访问,几段代码不能同时执行,就是互斥锁

package com.javase.thread;
/**
 * 这段代码主要演示互斥锁的使用
 *      使用synchronized修饰这段代码之后,只要他们同步监视器对象相同,那么这几段代码见就是互斥关系,多个线程不能同时执行这些代码
 * 
 * @author gupan
 *
 */
public class ThreadSyncMatual {
    /**
     * 线程t1和t2不能同时调用methodA或methodB方法,实现互斥关系
     * 
     * @param args
     */
    public static void main(String[] args) {
        Boo boo = new Boo();
        Thread t1 = new Thread() {
            public void run() {
                boo.methodA();
            }
        };
        
        Thread t2 = new Thread() {
            public void run() {
                boo.methodB();
            }
        };
        
        t1.start();
        t2.start();
    }
}

class Boo{
    public void methodA(){
        try{
            Thread t = Thread.currentThread();
            System.out.println(t.getName() + "正在执行A方法");
            Thread.sleep(1000);
            System.out.println(t.getName() + "执行A方法完毕");
        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
    
    public void methodB(){
        try{
            Thread t = Thread.currentThread();
            System.out.println(t.getName() + "正在执行B方法");
            Thread.sleep(1000);
            System.out.println(t.getName() + "执行B方法完毕");
        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

以上是关于Java04 线程同步问题解决——线程锁(同步锁互斥锁)的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程同步锁的理解

Java同步—线程锁和条件对象

Java线程 — 线程同步及安全问题

线程同步之读写锁

java并发之线程同步(synchronized和锁机制)

线程共享数据的安全问题