多线程问题



线程同步

同步代码块

  • 同步代码块中锁的对象可以是任意对象,但必须是唯一的,用static修饰

静态方法中的同步代码块锁的是类对象:People.class

售票操作

class Worker extends Thread{
    private static int ticket = 100;
    public static Object obj = new Object();
    @Override
    public void run() {
        show();
    }
    public void show(){
        synchronized (obj){
            while (true){
                if (ticket<=0){
                    System.out.println("票已售罄");
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("这是第"+ticket--+"张票");
            }
        }
    }
}

测试

public class ShowTicket {
    public static void main(String[] args) {
        Worker worker01 = new Worker();
        Worker worker02 = new Worker();
        Worker worker03 = new Worker();
        Worker worker04 = new Worker();
        worker01.start();
        worker02.start();
        worker03.start();
        worker04.start();
    }
}

同步方法

非静态同步方法————锁住单个对象
  • 锁的对象是:当前实例对象。

若线程A进入非静态同步方法,进入后休眠1秒,此时释放CPU执行权,但不释放锁。
其他同一对象的线程虽然拿到了执行权,但是无法进入同步方法进行执行。

public class synchronizedMethod {
    public static void main(String[] args) {
        HappyNewYear thread = new HappyNewYear();
        // 两个线程中传入同一个线程对象
        Thread thread1 = new Thread(thread);
        Thread thread2 = new Thread(thread);
        thread1.start();
        thread2.start();
    }
}
class HappyNewYear implements Runnable{
    @Override
    public synchronized void run() {
        System.out.println("HAPPY");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("NEW");
        System.out.println("YEAR");
    }
}

输出:
HAPPY NEW YEAR
HAPPY NEW YEAR

总结:两个线程中传入同一个线程对象,不会出现线程安全问题
若传入该类的其他对象,会导致线程不安全——————出错

静态同步方法————锁住该类(类中所有对象)
  • 锁的对象时:类型.class

若线程A进入静态同步方法,进入后休眠1秒,此时锁住了这个方法所在类,此时释放CPU执行权,但不释放锁。
其他对象的线程虽然拿到了执行权,但是无法进入该类中执行方法,需要等待获取类的锁

class HappyNewYear02 implements Runnable{
    @Override
    public synchronized void run() {
       method();
    }
    public static synchronized void method(){
        System.out.print("我");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.print("  是");
        System.out.println("  小学生");
    }
}

测试

public class synchronizedMethod02 {
    public static void main(String[] args) {
        HappyNewYear02 thread = new HappyNewYear02();
        HappyNewYear02 thread02 = new HappyNewYear02();
        // 两个线程中传入不同的对象
        Thread thread1 = new Thread(thread);
        Thread thread2 = new Thread(thread02);
        thread1.start();
        thread2.start();
    }
}

总结:两个线程中传入可传入一个类的不同对象,也不会出现线程安全问题

Lock锁

  • 显式锁,需要手动开启和关闭,且仅为代码块锁
class Lock implements Runnable{
    int num = 10;
    private static ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            if (num<=0){
                break;
            }
            lock.lock();
            try{
                System.out.println(num--);
            }finally {
                lock.unlock();
            }
        }
    }
}

测试

public class LockDemo {
    public static void main(String[] args) {
        Lock lock = new Lock();
        new Thread(lock).start();
        new Thread(lock).start();
    }
}

wait ( ) 与notify ( )

  • sleep(long)————不释放锁
  • wait()————释放锁,搭配notifyAll(),在同步锁中使用
  1. 其中线程1先获得A锁,然后调用wait()进入阻塞状态,释放A锁
  2. 线程2获得了B锁,然后获得了A锁(可重入锁原理),最后唤醒线程1的阻塞状态
  3. 线程1最后获得了B锁
public class WaitDemo {
    public static void main(String[] args) {
        new Thread(new LockA()).start();
        new Thread(new LockB()).start();

    }
}
class LockA implements Runnable{

    @Override
    public void run() {
        synchronized ("A"){
            System.out.println("线程1获得了A锁");
            try {
                "A".wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        synchronized ("B"){
            System.out.println("线程1获得了A和B锁");
        }
    }
}

class LockB implements Runnable{
    @Override
    public void run() {
        synchronized ("B"){
            System.out.println("线程2获得了B锁");
            synchronized ("A"){
                System.out.println("线程2获得了B和A锁");
                "A".notifyAll();
            }
        }
    }
}

输出:
线程1获得了A锁
线程2获得了B锁
线程2获得了B和A锁
线程1获得了A和B锁

单例模式

懒汉单例————线程不安全

  • 当多个线程同时进入getInstance()方法时,会创建多个实例
public class LazyDemo {
    public static void main(String[] args) {
        // 多线程,同时进入getInstance方法
        new Thread(new Factory02()).start();
        new Thread(new Factory02()).start();
        new Thread(new Factory02()).start();
    }
}
class Lazy{
    private Lazy(){}
    private static Lazy man = null;
    public static Lazy getInstance(){
        if (man==null){
           man = new Lazy();
        }
        return man;
    }
}
class Factory02 implements Runnable{
    @Override
    public void run() {
        Lazy instance = Lazy.getInstance();
        System.out.println(instance.hashCode());
    }
}

输出:
258124931
237601000
595124058

懒汉单例————线程安全

  • 双重检查
class Single{
    private Single(){}
    private static volatile Single man;
    public static Single getInstance(){
        if (man==null){
            synchronized (Single.class){
                if (man==null){
                    man = new Single();
                }
            }
        }
        return man;
    }
}
class Factory03 implements Runnable{

    @Override
    public void run() {
        Single instance = Single.getInstance();
        System.out.println(instance.hashCode());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值