Java学习笔记Day18:线程(2)

线程安全问题

私有数据不需要考虑线程安全问题
共享数据才是导致线程不安全的罪魁祸首

线程之间会因为调度的原因,穿插执行
数据的方法有一定的特殊规则

线程不安全的因素:
1. 代码的原子性:automic一段代码运行期间不能被分割
2. 内存的可见性:程序运行过程中:Java只保证了,单线程情况下,工作内存中的数据是正确的,如果要保证多线程情况下,可以看到线程的工作情况(内存的变化)就需要保证变量的可见性问题
3. 代码的重排序

解决线程安全问题的方法:

1.同步代码块

public class RunnableImpl implements Runnable{
    private int ticket=100;
    Object obj=new Object();
    public void run(){
        while(true){
            synchronized(obj){
                if(ticket>0){
                    try{
                        Thread.sleep(10);
                    }catch(TnterruptedException e){
                        e.printStackTrace();
                    }
                                                                             System.out.println(Thread.currentThread().getName()+"--->正在卖第"+ticket+"张票");
            ticket--;
            }
        }
    }
}

同步中的线程,没有执行完毕不会释放,同步外的线程没有锁进不去同步

2同步方法

使用步骤:

  • 把访问了共享数据的代码抽取出来,放到一个方法中

  • 在方法上添加synchronized修饰符

public class RunnableImpl implements Runnable{
    private int ticket=100;
    Object obj=new Object();
    public void run(){
        while(true){
            payTicket();
            }
        }
    }
    public synchronized void payTicket(){
       if(ticket>0){
           try{
              Thread.sleep(10);
           }catch(TnterruptedException e){
              e.printStackTrace();
            }
                                                                             System.out.println(Thread.currentThread().getName()+"--->正在卖第"+ticket+"张票");
            ticket--;
        }
    }
}
      

3.静态同步方法
静态方法的所对象是本类的class属性–》class文件对象(反射)

public class RunnableImpl implements Runnable{
    private static int ticket=100;
    Object obj=new Object();
    public void run(){
        while(true){
            payTicket();
            }
        }
    }
    public static synchronized void payTicket(){
       if(ticket>0){
           try{
              Thread.sleep(10);
           }catch(TnterruptedException e){
              e.printStackTrace();
            }
                                                                             System.out.println(Thread.currentThread().getName()+"--->正在卖第"+ticket+"张票");
            ticket--;
        }
    }
}

4.Lock锁

java.util.concurrent.locks.Lock接口
Lock实现提供了比使用synchronized方法和语句可获得更广泛的锁定操作
Lock接口中的方法:
void lock() 获取锁
void unlock()释放锁
java.util.concurrent.locks.ReentrantLock implements Lock接口

使用步骤:
1. 在成员位置创建一个ReentrantLock对象
2. 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
3. 在可能会出现安全问题的代码前调用Lock接口中的方法Lock获取锁

public class RunnableImpl implements Runnable{
    private static int ticket=100;
    Lock l=new ReentrantLock();
    Object obj=new Object();
    public void run(){
        while(true){
               l.lock();
                if(ticket>0){
                    try{
                        Thread.sleep(10);
           System.out.println(Thread.currentThread().getName()+"--->正在卖第"+ticket+"张票");
                        ticket--;
                    }catch(TnterruptedException e){
                        e.printStackTrace();
                    }finally{
                        l.unlock();
                    }
                }
        }
    }
}

生产消费案例
Object类中的方法
1. void wait() 在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待
2. void notify()唤醒在此对象监视器上等待的单个线程,会继续执行wait方法之后的代码

public class ProduceExpend {
    public static void main(String[] args) {
        Object obj=new Object();
        new Thread(){
            public void run(){
                while(true) {
                    synchronized (obj) {
                        System.out.println("告知老板要的包子数量");
                        try {
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("包子做好,开吃");
                        System.out.println("----------------------");
                    }
                }
            }
        }.start();
        new Thread(){
            public void run(){
                while(true){
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj){
                        System.out.println("5秒钟包子做好了,可以吃包子了");
                        obj.notify();
                    }
                }


            }
        }.start();
    }
}

进入到TimeWaiting(计时等待)有两种方式:
1. 使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
2. 使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态

唤醒方法:
1. void notify()唤醒此对象监视器上等待的单个线程
2. void notifyAll()唤醒在此对象监视器上等待的所有线程

线程池

java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
Executors类中的静态方法:
static ExecrtorService newFixedThreadPool(Int nThreads):创建一个可重用固定线程数的线程池

java.util.concurrent.ExecutorSevice:线程池接口
用来从线程池中获取线程,调用start方法,执行线程任务
submit(Runnable task)提交一个Runnable任务用于执行
void shutdown()关闭/销毁线程池的方法

线程池的使用步骤:
1. 使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务
3. 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
4. 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)

Lambda表达式

public class Lambda {
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println("创建了一个新线程");
        }).start();
    }
}

可以省略书写(大括号,分号,return,参数类型相同可以省略,参数只有一个也可以省略 )

public class Lambda {
    public static void main(String[] args) {
        new Thread(()->System.out.println("创建了一个新线程")).start();
    }
}

volatile变量修饰符
1. 可见性
2. 可以保证重排序

对象中的等待集(wait set)
Object.wait()
Object.notify()
Object.notifyAll()
用来做线程通信的

发布了118 篇原创文章 · 获赞 12 · 访问量 2581

猜你喜欢

转载自blog.csdn.net/qq_42174669/article/details/104102994
今日推荐