欢迎光临
我们一直在努力

JavaSE(十二)

5、线程安全问题

5.1共享资源的冲突

案例1:

        有几个人围着桌子吃蛋糕,当你举起叉子,准备叉起最后一块蛋糕,正当叉子碰到蛋糕的时候才发现它已经被另一个叉子叉起。这类现象就是共享资源(蛋糕)中出现的冲突,而两个人同时用叉子叉同一块蛋糕的过程我们就称为并发。

案例2:

        转账案例:

5.2问题分析

线程安全问题都是由全局变量及静态变量引起的。

若每个线程对全局变量、静态变量只读,不写,一般来说,这个变量是线程安全的;

若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

综上所述,线程安全问题根本原因: 

 多个线程在操作共享的数据

5.3问题解决线程同步

专业:即线程操作共享数据时其他线程不可以参与执行,直到该线程完成操作;

民间:同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说;

为了保证每个线程都能正常执行共享资源操作,Java引入了7种线程同步机制:

        1)同步代码块(synchronized

        2)同步方法(synchronized

        3)同步锁(ReenreantLock

        4)特殊域变量(volatile

        5)局部变量(ThreadLocal

        6)阻塞队列(LinkedBlockingQueue

        7)原子变量(Atomic

​​​​​​​同步代码块synchronized):

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

语法:

        synchronized(钥匙){

        需要同步操作的代码

        }

        钥匙:钥匙可以是任意类型;多个线程要使用同一把钥匙。

注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。

​​​​​​​同步方法synchronized):

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

格式:

        public synchronized void method(){

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

        }

同步锁是谁?

        对于非static方法,同步锁就是this

        对于static方法,同步锁是当前方法所在类的字节码对象(类名.class)

​​​​​​​同步锁ReentrantLock):

        java.util.concurrent.locks.Lock 提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

方法:

        public void lock() :加同步锁。

        public void unlock() :释放同步锁。

案例:

package com.hg.Thread.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TicketTest {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"窗口-1").start();
new Thread(ticket,"窗口-2").start();
new Thread(ticket,"窗口-3").start();

}
}

class Ticket implements Runnable{
private Integer ticketNum =10;
private Object obj = new Object(); //唯一钥匙
private Lock lock = new ReentrantLock();

@Override
public void run() {
while (true){
sellTicket();
}
}
//方式二:同步方法,此时this是钥匙
private /*synchronized*/ void sellTicket(){
//方式一:同步代码块
//synchronized (obj){ //锁
lock.lock(); //方式三:和同步代码块一样
try {
if (ticketNum > 0){
System.out.println("当前" + Thread.currentThread().getName() + "售票:" + ticketNum–);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally {
lock.unlock();
}
}
}

6、死锁

6.1什么是死锁

  • 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
  • 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
  • 6.2产生死锁的原因

     线程 1 持有r1,等待获取r2;

     线程 2 持有r2,等待获取r1;

     两个线程都不会释放已持有的锁,且都在等待对方的锁,形成循环等待,最终导致死锁。

    6.3怎样避免死锁

      避免一个线程同时获取多个锁;

    7、线程通讯

    7.1​​​​​​​为什么要线程通信

    多个线程并发执行时,在默认情况下CPU是随机切换线程的,有时我们希望CPU按我们的规律执行线程,此时就需要线程之间协调通信。

    7.2方式

            常用方法:

            wait():一旦执行此方法,当前线程就进入阻塞状态,并释放cpu资源。

            notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。

            notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。

    案例:两个线程交替打印1-100

    package com.hg.Thread.Communication;

    public class CommTest {
    public static void main(String[] args) {
    Comm comm = new Comm();
    new Thread(comm).start();
    new Thread(comm).start();
    }
    }

    class Comm implements Runnable{
    private int i = 1;

    @Override
    public void run() {
    while (true) {
    synchronized (this) {
    this.notify(); //唤醒等待此钥匙的线程
    if (i <= 100) {
    System.out.println(Thread.currentThread().getName() +": " + i++);
    }else {
    break;
    }
    try {
    this.wait(); //该线程让出钥匙并进入阻塞(等待)状态(让出钥匙后必须被notify(),否则一直拿不到钥匙)
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }

    8、线程的生命周期

    8.1新建

            new关键字创建了一个线程之后,该线程就处于新建状态         JVM为线程分配内存,初始化成员变量值

    8.2就绪

            当线程对象调用了start()方法之后,该线程处于就绪状态         JVM为线程创建方法栈和程序计数器,等待线程调度器调度

    8.3运行

            就绪状态的线程获得CPU资源,开始运行run()方法,该线程进入运行状态

    8.4阻塞

            在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中         止自己的执行,进入阻塞状态

    8.5死亡

    线程会以如下3种方式结束,结束后就处于死亡状态:         run()或call()方法执行完成,线程正常结束。         线程抛出一个未捕获的Exception或Error。         调用该线程stop()方法来结束该线程,该方法容易导致死锁,不推荐使用。

    赞(0)
    未经允许不得转载:171主机测评 » JavaSE(十二)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址