Java基础-day16-多线程数据安全隐患·实现Runnable接口·线程池

Day17

1.多线程数据安全隐患
怎么产生的?线程的随机性+访问延迟
2.以后如何判断程序有没有线程安全问题?
在多线程程序中 + 有共享数据 + 多条语句操作共享数据
1)单线程程序不会出现多线程抢占资源的情况
2)如果没有共享数据,互不干涉,也不会出现数据的安全问题
3)多条语句操作了共享数据,而多条语句的执行是需要时间的,存在延迟
所以这个时间差导致了数据的安全问题

================================
2.多线程安全问题解决方案
1.加锁的位置:
容易出现线程安全问题的资源
2.加锁的范围
太大:整个程序都是同步的,都得排队,效率太低,失去了多线程的效果
太小:要保护的资源锁不住
*同步与异步:
同步:类似于排队的效果,同一时刻只能有一个线程占有资源,其他线程排队
**好处:是线程安全的
**坏处:排队的效率低
异步:不排队,多线程的效果,各个线程之间互相不等待,抢占资源
**好处:抢占资源效率高
**坏处:可能会出现数据安全隐患
死锁:一定要注意避免,手动加锁后一定要记得释放锁

================================
售票案例–4个窗口共计售票100张

/双重校验一:有票的时候再卖票增加一个判断
* synchronized(锁对象){容易发生数据安全问题的代码}
* 在同步代码块中,同一时刻,同一资源只能被一个线程独享,排队
* 注意:锁对象唯一,如果不唯一,还会发生安全问题
* 如果是继承的方式的话,锁对象一般使用本类的字节码对象
/
* 同步方法:用synchronized修饰方法就变成了同步方法–不常用
继承Thread
//synchronized (TicketThread2.class) {

package cn.tedu.tickets;
//解决继承下的多线程数据安全
public class TestExtends2 {
    public static void main(String[] args) {
        TicketThread2 t1 = new TicketThread2("窗口1");
        TicketThread2 t2 = new TicketThread2("创口2");
        TicketThread2 t3 = new TicketThread2("窗口3");
        TicketThread2 t4 = new TicketThread2("窗口4");
		//6.以多线程的方式启动--将线程加入到就绪队列
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

//自定义多线程类
class TicketThread2 extends Thread{
//定义变量设置票数,设置为静态资源属于类资源,被所有对象共享,只有一份
    public TicketThread2(){
        super();
    }
    public  TicketThread2(String name){
        super(name);
    }
    static int tickets = 100;
    @Override
    public void run() {
        //super.run();
        while (true){
            /*双重校验一:有票的时候再卖票增加一个判断
            * synchronized(锁对象){容易发生数据安全问题的代码}
            * 在同步代码块中,同一时刻,同一资源只能被一个线程独享,排队
            * 注意:锁对象唯一,如果不唯一,还会发生安全问题
            * 如果是继承的方式的话,锁对象一般使用本类的字节码对象*/
            synchronized (TicketThread2.class) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (tickets > 0) {
                    //4.1输出当前正在卖票的线程名+票数
                    System.out.println(getName() + "=" + tickets--);
                }
                if (tickets <= 0) break;
            }
        }
    }
}

实现Runnable接口

//实现接口的方式,不限制锁对象的类型【类型、也可以使用字节码对象】,只需要保持锁对象的唯一
//也就是提取到成员位置创建对象即可,因为target对象只创建一次

//synchronized (new Object) {
//注意锁对象必须唯一,此种方式不正确
//原因:每个线程执行run()时,都会new一个锁对象

package cn.tedu.tickets;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//解决实现接口下的多线程数据安全
public class TestRunnable2 {
    public static void main(String[] args) {
        TicketsRunnabble2 target = new TicketsRunnabble2();
        Thread t1 = new Thread(target, "窗口1");
        Thread t2 = new Thread(target, "窗口2");
        Thread t3 = new Thread(target, "窗口3");
        Thread t4 = new Thread(target, "窗口4");


        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
//自定义多线程售票业务类

class TicketsRunnabble2 implements Runnable{

    int tickets = 100;
    //实现接口的方式,不限制锁对象的类型,只需要保持锁对象的唯一
    //也就是提取到成员位置创建对象即可,因为target对象只创建一次

    Object o = new Object();

    @Override
    /*被synchronized修饰的方法称为同步方法*/
    //synchronized public void run() {
     public void run() {

            while(true){
                //synchronized (new Object) {
                //注意锁对象必须唯一,此种方式不正确
                //原因:每个线程执行run()时,都会new一个锁对象
                synchronized (o) {//同步代码块
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (tickets > 0) {
                        System.out.println(Thread.currentThread().getName() + "=" + tickets--);
                    } else {
                        break;
                    }
                }
            }
    }
}

线程池
存放线程的池子
创建线程池的工具类:Executors.newFixedThreadPool(int自定义的线程数)
启动线程池中的线程:pool.execute(target目标业务对象)
TIPS:线程池会自动管理线程,线程池目前单机测试不关闭,需要手动关闭

//解决实现接口下的多线程数据安全
public class TestRunnable2 {
    public static void main(String[] args) {
        TicketsRunnabble2 target = new TicketsRunnabble2();
//        Thread t1 = new Thread(target,"窗口1");
//        Thread t2 = new Thread(target,"窗口2");
//        Thread t3 = new Thread(target,"窗口3");
//        Thread t4 = new Thread(target,"窗口4");
//
//
//        t1.start();
//        t2.start();
//        t3.start();
//        t4.start();
    /*9.线程池ExectorService:用于存储线程池子,把新建/启动/关闭线程都交给池来做
    * 10.Executors 用于创建线程池的工具类,
    * newFixedThreadPool(线程数)--创建一个指定线程数的线程池*/
        //创建线程池对象,线程池容纳的线程数指定为5
        ExecutorService pool = Executors.newFixedThreadPool(5);
        for (int i =1;i<6;i++){
            //execute(target);让线程池中的线程来执行任务,每次调用都会启动一个线程
            //方法的参数是要执行的自定义业务,也就是目标对象taeget

            pool.execute(target);
        }
    }
}

===============================

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值