zoukankan      html  css  js  c++  java
  • 黑马程序员

    多线程
    进程:一个正在执行中的程序,每一个进行执行,都有一个执行顺序,该顺序就是一个执行路径,或者加一个执行单元
    线程:就是进程中的一个独立的执行路径,一个进程中至少有一个线程。
    java vm启动的时候会有一个进程java.exe。
    该进程中至少一个线程负责执行java程序的执行,
    而且这个线程运行的代码存在于main方法中。
    该线程称之为主线程。
    扩展:其中更细节说明java vm启动不止一个线程,还有赋值垃圾回收机制的线程。

    创建线程的实现步骤:
    方法一
    1.定义一个类继承Thread类
    2.覆写Thread类中的run方法
    3.在主函数中直接创建Thread的子类
    4.运行start调用线程

    方法二
    1.定义一个类实现Runnable接口
    2.在此类中写入需要多线程运用的代码
    3.将 Runnable 接口的子类对象作为实际参数传递给 Thread 类的构造函数。
    4.运行start调用线程

    以下代码简单演示创建线程的两种方式

    class Demo1 extends Thread {                //第一种方式继承Thread类
        private String name;
        Demo1(String name){
            this.name = name;
        }
        public void run() {                     //覆写run方法    
            for (int i=1;i<10 ;i++ ){           //循环9次打印当前线程名字
                System.out.println(name+"...."+i+Thread.currentThread().getName());
                //Thread.currentThread().getName() 获取 当前 运行中 线程的名字
            }
        }
    }
    class Demo2 implements Runnable {           //第二种方式,实现Runnable接口
        private String name;
        Demo2(String name){
            this.name = name;
        }
        public void run(){                      //覆写run方法
            for (int i=1;i<10 ;i++ ){           //循环9次打印当前线程名字
                System.out.println(name+"...."+i+Thread.currentThread().getName());
                //Thread.currentThread().getName() 获取 当前 运行中 线程的名字
            }
        }
    }
    public class AThreadDemo {
        public static void main(String[] args) {
            /*
            创建线程的目的是为了开启一条新的执行路径,去运行指定的代码和其他代码实现同时运行。
            而运行的 指定代码就是这个执行路径的任物。
            jvm创建的主线程的任物都定义在主函数中。
    
            而自定义的新城用于描述线程,线程是需要任物的,所以Thread类也对任物的描述。
            这个任务就通过Thread类中的run方法来体现,也就是run方法就是封装自定义线程运行任务的函数。
            run方法中定义的就是线程要运行的任物代码。
            */
            Demo1 d1 = new Demo1("d1");
            Demo2 d2 = new Demo2("d2");
            Thread t1 = new Thread(d1);
            Thread t2 = new Thread(d2);
            t1.start();                   //开启多线程,调用run方法
            t2.start();
            //t1.run();
            //t2.run();                   //直接调用run方法,没有线程的随机性.        
        }
    }

    运行结果

    由运行结果可知,d1线程和d2线程交替运行,两个线程均创建成功,并在主线程中调用正常。

    线程安全

    线程安全问题产生的原因:
    1.多个线程在操作共享的数据。
    2.操作共享数据的线程代码有多条。
    当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算就会导致线程安全问题的产生。
    问题解决思路:
    将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不能参与运算。
    必须当前线程吧这些代码都执行完毕后,其他线程才可以产于运算。
    java中用同步代码块或同步函数解决该问题。
    同步代码块的格式:
    synchronized(对象) //此处对象为任意对象,可直接用Objct
    {
    需要被同步的代码
    }
    同步函数就是用synchronized修饰函数
    注意:定义同步函数必须注意需要同步的代码是哪些。
    同步的前提:同步中必须有多个线程,并使用同一个锁(即使用的同一个对象)
    如果同步函数被静态修饰后,使用的锁是什么?
    通过验证,发现不是this,因为静态方法不可以定义this
    静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
    类名.class 该类对象的类型是Class。
    静态的同步方法,使用的锁是该方法所在类的字节码所在对象。

    多线程练习
    需求:有20张票,需要在4个窗口卖完
    编程注意:必须保证各个线程是共用的一个num数据

    以下代码演示同步代码块解决安全问题的方法

    步骤:
    1:定义一个Thread类,包含了对卖票的代码
    2:在定义Thread类时,将票数num定义为静态变量,从而保证不同线程运行时是对相同的100张票进行调用。
    3:在主函数中定义4个线程对象,并用start方法开启线程。

    //实现代码一:(应用同步代码块)
    class Ticket1 extends Thread{
        private static int num = 20;    //将num储存在方法区,由4个线程共用。
        Object obj = new Object();       //对象必须定义在run外
        public void run(){
            while (num>0){
                synchronized(obj){       //此处需要被同步的代码存在多条,必须定义同步代码块        
                    if(num>0){
                        System.out.println(Thread.currentThread().getName()+"..."+num--);
                    }
                }
            }
        }
    }
    public class BThreadTicket1{
        public static void main(String[] args){
            Ticket1 t1 = new Ticket1();
            Ticket1 t2 = new Ticket1();
            Ticket1 t3 = new Ticket1();
            Ticket1 t4 = new Ticket1();
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }

    运行结果

    由运行结果可以看出,4个线程交替运行,出售这20张票。

    第二种实现方式

    步骤:
    1:定义一个包含票数,及买票功能的对象Ticket2,并对其扩展Runnable接口,在其中覆写run方法。
    2:在主函数中创建票对象
    3:在主函数中创建4个线程对象,并将票对象作为它们的构造函数参数
    4:运行start方法开启线程

    class Ticket2 implements Runnable  {
        private int num = 20;    
        public void run() {
            while (num>0){
                synchronized(Ticket2.class){
                    if(num>0){
                        try{Thread.sleep(10);}catch(Exception e){}
                        System.out.println(Thread.currentThread().getName()+"..."+num--);
                    }
                }
            }
        }
        //定义同步函数,函数需要被对象调用,那么函数都有一个所属对象引用,就是this
        //所以同步函数使用的锁是this    
    }
    public class BThreadTicket2 {
        public static void main(String[] args){
            //单独建立一个票的对象,4个线程均是对该票对象的调用
            Ticket2 t2 = new Ticket2();
            Thread th1 = new Thread(t2);
            Thread th2 = new Thread(t2);
            Thread th3 = new Thread(t2);
            Thread th4 = new Thread(t2);
            th1.start();
            th2.start();
            th3.start();
            th4.start();
        }
    }

    运行结果:

    由运行结果可以看出,4个线程交替运行,出售这20张票。

    多线程应用
    需求:
    生产者生成15个产品 ,并且每生产一个产品,消费者就消费一个。

    此代码用了新的同步方式,定义锁;并通过定义标记,使消费线程和成产线程交替运行。

    import java.util.concurrent.locks.*;
    class Resource{                                  //定义商品
        private String name;                         //定义成员变量
        private int count = 1;                        
        private boolean flag = false;                //定义标记
        //Lock方法
        private Lock lock = new ReentrantLock();     //定义锁
        private Condition condition_pro = lock.newCondition();    
        private Condition condition_cou = lock.newCondition();
        public void set(String name) throws InterruptedException //定义生产动作
        {
            if (count<15){
                lock.lock();                            //获取锁
                try{
                    while (flag)                        //注意此处用while是因为需要循环判断
                        condition_pro.await();          //使当前线程等待
                    this.name = name;                   //生成一个产品
                    System.out.println(Thread.currentThread().getName()+name+"生产商品"+(count++)+"----");
                    flag = true;                        //改变标记,是下次循环时能够让生产者等待
                    condition_cou.signal();             //唤醒另一个在等待的线程
                }
                finally {
                    lock.unlock();                      //释放锁
                }
            }    
        }
        public void show() throws InterruptedException  //定义消费动作
        {
            if (count<15){
                lock.lock();                            //获取锁
                try{
                    while (!flag)                      
                        condition_cou.await();          //让当前线程等待
                    System.out.println(Thread.currentThread().getName()+"----消费商品"+name+(count-1));
                    flag = false;                        //消费一个对象,并改变标记
                    condition_pro.signal();                 //唤醒另一个正在等待的线程
                }
                finally {
                    lock.unlock();                       //释放锁
                }
            }
        }
    }
    class Producter implements Runnable{           //定义生成者
        private Resource r;
        Producter (Resource r){                    //将商品传给生产者   
            this.r = r;
        }
        public void run(){                         //覆写run方法
            while (true){
                try{
                    r.set("商品");                 //调用生产动作,生产商品
                }
                catch (InterruptedException e){
                }
            }
        }
    }
    class Coustomor implements Runnable{           //定义消费者
        private Resource r;                        
        Coustomor (Resource r){                       //将商品传给消费者
            this.r = r;
        }
        public void run(){                         //覆写run方法
            while (true){
                try{
                    r.show();                      //调用消费动作消费商品
                }
                catch (InterruptedException e){
                }
            }
        }
    }
    public class CThreadRe{
        public static void main(String[] args) {
            Resource r = new Resource();            //定义商品对象
            Producter pro = new Producter(r);       //将商品传给生产者
            Coustomor cou = new Coustomor(r);        //将商品传给消费者
            Thread t1 = new Thread(pro);            //定义两个生产线程
            Thread t2 = new Thread(pro);
            Thread t3 = new Thread(cou);            //定义两个消费线程
            Thread t4 = new Thread(cou);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }

    运行结果

    上面代码实际上是运用流多线程通讯的思想。

    线程间通讯:
    其实就是多个线程在操作同一个资源,但操作动作不同
    实现核心:
    多线程的等待唤醒机制

    步骤:
    1:设置flag标记变量
    2: 在同步代码块中设置 锁名.wait() 而且锁名.wait()必须被 try{}catch{}(或是锁对象.await())

    3: 在同步代码块中设置 锁名.notify().(或是锁对象.singal())
    注意:设置wait() notify()时,运用flag变量做控制

  • 相关阅读:
    java8新特性学习五(接口中的默认方法与静态方法)
    java8新特性学习四(Optional类)
    [record]WebLogic域之创建-文本界面
    [转]java 关于httpclient 请求https (如何绕过证书验证)
    数组与串,串的实现,KMP,BF算法
    git
    nginx设置重写规则
    深入PHP 第三章笔记
    进程和线程
    JS 获取 iframe内元素,及iframe与html调用
  • 原文地址:https://www.cnblogs.com/myblog-cl/p/4746319.html
Copyright © 2011-2022 走看看