【学习笔记】多线程的实现和安全问题

概念

线程

负责java程序代码执行的控制单元,叫做线程。

 

并行和并发

并行是指两个或者多个事件在同一时刻发生。

而并发是指两个或多个事件在同一时间建个发生。

并行是发生在不同实体上的多个事件。

并发是同一实体上的多个事件。

 

普通解释:

并发:交替做不同事情的能力

并行:同时做不同事情的能力

专业术语:

并发:不同的代码块交替执行

并行:不同的代码块同时执行

 

多线程实现方式

继承Thread

  1. 定义类继承Thread。

  2. 复写Thread类中的Run方法。

  3. 调用线程的start()方法,启动线程。

示例 

public class ThreadDemo {
    public static void main (String[] args) {
        MyThread myThread1 = new MyThread();
        myThread1.setName("线程1");

        MyThread myThread2 = new MyThread();
        myThread2.setName("线程2");

        myThread1.start();
        myThread2.start();
    }
}

 

public class MyThread extends Thread {
    @Override
    public void run () {
        for (int i=0;i<10;i++){
            System.out.println("线程id--->"+Thread.currentThread().getId()+"===线程名称----->"+Thread.currentThread().getName() + ": 执行成功" );
        }
    }
}

复写run()方法的原因:
Thread类用于描述线程。其中run()方法用于存储线程要运行的代码。

实现Runnable接口

  1. 定义类实现Runnable接口。
  2. 重写Runnable中的run()方法,放入将要运行的代码。
  3. 通过Thread类建立线程对象,将实现Runnable接口的子类对象作为实参传递给Thread类的构造函数,指定Thread要运行的run()方法为子类对象的run()方法。
  4. 调用Thread类的start方法开启线程并调用Runnable接口子类的run()方法。

示例 

public class PayRunnableDemo {
    public static void main (String[] args) {

        PayRunnable p = new PayRunnable();

        Thread t1 = new Thread(p);
        Thread t2 = new Thread(p);
        Thread t3 = new Thread(p);
        Thread t4 = new Thread(p);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
public class PayRunnable implements Runnable {
    private static int stockNum = 100;

    public void run () {
        while (stockNum > 0){
            System.out.println("线程:"+Thread.currentThread().getName()+"下单======剩余库存"+ (--stockNum));
        }

    }
}

实现方式和继承方式的区别:
实现方式避免了单继承的局限性,在定义线程时建议使用实现方式。

继承Thread类,线程代码存放在Thread类的run()方法中。
实现Runnable,线程代码存放在实现接口子类的run()方法中。

 

多线程安全问题

问题的原因:
多个线程在操作同一个共享数据时,一个多条语句的线程只执行了一部分时,另外一个线程开始执行,导致共享数据发生错误。

解决方法:

同一时刻,只允许一条线程操作共享数据。执行过程中,其他线程不能参与执行。

要解决上述问题,可以使用java中提供的同步机制(synchronized)。

synchronized

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

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

1.普通同步方法,锁是当前实例对象

2.静态同步方法,锁是当前类的class对象

3.同步方法块,锁是括号里面的对象

 

同步代码块 : synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

synchronized(同步锁){

需要同步操作的代码

}

静态同步方法:synchronized(类.class)是全局锁,该类的所有实例对象共用一把锁。

synchronized(类.class){

需要同步操作的代码

}

同步方法 :使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。

public synchronized void method(){

可能会产生线程安全问题的代码

}

 

如果共享数据是同一个对象,以上三种方式都可以加锁。
但如果共享的数据是同一个类的不同实例对象的静态资源,只能使用静态同步方法加锁

synchronized(this)和使用synchronized修饰方法是对象锁,如果有多个对象就有相对应的多个锁。
synchronized(类.class)是全局锁,不管有几个对象就公用一把锁。

Lock锁

Lock锁与synchronized都可以用于保证线程安全。

Lock是一个接口,而synchronized是关键字。

synchronized会自动释放锁,而Lock必须手动释放锁。

Lock可以让等待锁的线程响应中断,而synchronized不会,线程会一直等待下去。

通过Lock可以知道线程有没有拿到锁,而synchronized不能。

Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

synchronized能锁住类、方法和代码块,而Lock是块范围内的

 

常用方法:

public void lock() :加同步锁。

public void unlock() :释放同步锁

public boolean tryLock();尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false。

示例

public class PayRunnableDemo {
    public static void main (String[] args) {

        PayRunnable p = new PayRunnable();

        Thread t1 = new Thread(p);
        Thread t2 = new Thread(p);
        Thread t3 = new Thread(p);
        Thread t4 = new Thread(p);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
public class PayRunnable implements Runnable {
    private static int stockNum = 100;
    private Lock lock = new ReentrantLock();

    public /*synchronized*/ void run () {
        lock.lock();
        while (stockNum > 0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程:"+Thread.currentThread().getName()+"下单======剩余库存"+ (--stockNum));
        }
        lock.unlock();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值