美文网首页
java核心技术-多线程基础

java核心技术-多线程基础

作者: 哦00 | 来源:发表于2018-11-25 21:29 被阅读0次

    进程、线程

    ​ 进程(Process) 是程序的运行实例。例如,一个运行的 Eclipse 就是一个进程。进程是程序向操作系统申请资源(如内存空间和文件句柄)的基本单位。线程(Thread)是进程中可独立执行的最小单位。一个进程可以包含多个线程。进程和线程的关系,好比一个营业中的饭店与其正在工作的员工之间的关系。

    1.1 线程的创建、启动与运行

    在 Java 中实现多线程主要用两种手段,一种是继承 Thread 类,另一种就是实现 Runnable 接口。(当然还有Callable和线程池)。下面我们就分别来介绍这两种方式的使用,其他请关注此博客下文。

    (1).继承Thread的类

    publicclassPrimeThreadextendsThread{//线程执行体@Overridepublicvoidrun(){for(inti =0; i <100; i++){if(i %2==0){                System.out.println(Thread.currentThread().getName() +"="+ i);            }        }            }    }

    publicclassTestThread{publicstaticvoidmain(String[] args){//新建一个线程PrimeThread p1 =newPrimeThread();//启动一个线程p1.start();                PrimeThread p2 =newPrimeThread();        p2.start();for(inti =0; i <100; i++ ){if(i %2==0){                System.out.println(Thread.currentThread().getName() +"="+ i);            }        }            }}

    (2).实现 Runnable接口

    publicclassTicketimplementsRunnable{privateintticket =100;@Overridepublicvoidrun(){while(ticket >0){            System.out.println(Thread.currentThread().getName() +"="+ --ticket);        }            }}

    publicclassTestThread2{publicstaticvoidmain(String[] args){                Ticket ticket =newTicket();//虽然是实现了Runnable接口 本质上只是实现了线程执行体 启动工作还是需要Thread类来进行Thread t1 =newThread(ticket,"售票窗口一");        t1.start();                Thread t2 =newThread(ticket,"售票窗口二");        t2.start();                Thread t3 =newThread(ticket,"售票窗口三");        t3.start();    }}

    两种实现方式的对比:

    1.从面向对象编程角度看:第一种创建方式(继承Thread类) 是一种基础继承的技术,第二种创建方式(以Runnable接口实例为构造器参数直接通过new创建Thread实例)是一种基础组合的技术。方式二不仅会避免单继承的尴尬,也会降低类与类之间的耦合性。

    2.从对象共享角度看:第二种创建方式意味着多个线程实例可以共享同一个Runnable实例。而第一种方式则需要依赖static关键字来完成操作。

    1.2 线程的控制

    Java的调度方法

    同优先级线程组成先进先出队列(先到先服务),使用时间片策略

    对高优先级,使用优先调度的抢占式策略

    Thread类的相关方法:

    sleep(long millis) : 是 Thread 类中的静态方法,使当前线程进入睡眠状态

    join() / join(long millis) : 是一个实例方法,使当前线程进入阻塞状态

    interrupt() : 中断阻塞状态的线程

    isAlive() : 判断当前线程是否处于存活状态

    yield() : 线程让步

    publicclassTestThread3{publicstaticvoidmain(String[] args)throwsException{                Thread t1 =newThread(newRunnable() {@Overridepublicvoidrun(){for(inti =1; i <100;i++){if(i %2==0){                    System.out.println(Thread.currentThread().getName() +"="+ i);                    }                }                                }        },"线程1");        t1.start();//线程1在 sleep之前就执行完了t1.sleep(10000);//join方法 迫使t2 必须等线程1 执行完 才能执行 然而 t1输出完自己的 睡着了 t2被迫等了10秒t1.join();        Thread t2 =newThread(newRunnable() {@Overridepublicvoidrun(){for(inti =1; i <100;i++){if(i %2!=0){                        System.out.println(Thread.currentThread().getName() +"="+ i);                    }                }            }        },"线程2");        t2.start();            }}

    1.3 线程的同步

    线程同步:模拟售票程序,实现三个窗口同时售票 100 张 (1.1案例)

    问题:当三个窗口同时访问共享数据时,产生了无序、重复、超额售票等多线程安全问题

    解决:将需要访问的共享数据“包起来”,视为一个整体,确保一次只能有一个线程执行流访问该“共享数据”

    Java给上述问题提供了几种相应的解决方法

    (1).同步代码块

    synchronized(同步监视器){

    //需要访问的共享数据

    }

    同步监视器 : 俗称“锁”,可以使用任意对象的引用充当,注意确保多个线程持有同一把锁(同一个对象)

    (2).同步方法

    同步方法 : 在方法声明处加 synchronized. 注意:非静态同步方法隐式的锁 ---- this

    例如:

    public synchronized void show(){}

    (3).同步锁

    同步锁 : Lock 接口

    同步代码块

    publicclassSafeTicketimplementsRunnable{privateintticket =100;@Overridepublicvoidrun(){while(true){//使用同步代码块synchronized(this) {if(ticket >0){                        System.out.println(Thread.currentThread().getName() +" 完成售票,余票:"+ --ticket);                }            }                                }    }}

    同步方法:

    publicclassSafeTicketimplementsRunnable{privateintticket =100;@Overridepublicvoidrun(){while(true){//使用同步代码块sale();                    }    }publicsynchronizedvoidsale(){if(ticket >0){            System.out.println(Thread.currentThread().getName() +" 完成售票,余票:"+ --ticket);        }    }    }

    同步锁

    publicclassSafeTicketimplementsRunnable{privateintticket =100;privateLock l =newReentrantLock();@Overridepublicvoidrun(){while(true){            l.lock();try{if(ticket >0){                    System.out.println(Thread.currentThread().getName() +" 完成售票,余票:"+ --ticket);                }            }finally{                l.unlock();//释放锁}                    }    }    }

    死锁

    死锁 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于 死锁 状态或系统产生了 死锁 ,这些永远在互相等待的进程称为 死锁 进程

    publicclassTestDeadLock{publicstaticvoidmain(String[] args){finalStringBuffer s1 =newStringBuffer();finalStringBuffer s2 =newStringBuffer();newThread() {publicvoidrun(){synchronized(s1) {                    s2.append("A");try{                        Thread.sleep(10);                    }catch(InterruptedException e) {                        e.printStackTrace();                    }synchronized(s2) {                        s2.append("B");                        System.out.print(s1);                        System.out.print(s2);                    }                }            }        }.start();newThread() {publicvoidrun(){synchronized(s2) {                    s2.append("C");synchronized(s1) {                        s1.append("D");                        System.out.print(s2);                        System.out.print(s1);                    }                }            }        }.start();    }}

    1.4 线程的通信

    在 java.lang.Object 类中:

    wait() : 使当前“同步监视器”上的线程进入等待状态。同时释放锁

    notify() / notifyAll() : 唤醒当前“同步监视器”上的(一个/所有)等待状态的线程

    注意:上述方法必须使用在同步中

    场景1:使用两个线程打印 1-100 线程1和线程2交替打印

    publicclassMyThreadimplementsRunnable{inti =0;@Overridepublicvoidrun(){while(true){synchronized(this) {this.notify();if(i <=100){                    System.out.println(Thread.currentThread().getName() +"="+ i++);                }try{this.wait();                }catch(InterruptedException e) {                }            }                    }            }    }

    publicclassTestThread4{publicstaticvoidmain(String[] args){        MyThread myThread =newMyThread();                Thread t1 =newThread(myThread,"线程1");        Thread t2 =newThread(myThread,"线程2");                t1.start();        t2.start();    }}

    经典例题:生产者/消费者问题

    生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,

    店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,

    如果店中有空位放产品了再通知生产者继续生产;

    如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。

    publicclassTestProduct{publicstaticvoidmain(String[] args){        Clerk clerk =newClerk();                Productor pro =newProductor(clerk);        Customer cus =newCustomer(clerk);newThread(pro).start();newThread(cus).start();    }}// 店员classClerk{privateintproduct;// 进货publicsynchronizedvoidgetProduct(){if(product >=20) {            System.out.println("产品已满!");try{                wait();            }catch(InterruptedException e) {            }                    }else{            System.out.println("生产者生产了第"+ ++product +" 个产品");                        notifyAll();        }    }// 卖货publicsynchronizedvoidsaleProduct(){if(product <=0) {            System.out.println("缺货!");try{                wait();            }catch(InterruptedException e) {            }                    }else{            System.out.println("消费者消费了第"+ --product +" 个产品");                        notifyAll();        }    }}// 生产者classProductorimplementsRunnable{privateClerk clerk;publicProductor(){    }publicProductor(Clerk clerk){this.clerk = clerk;    }publicClerkgetClerk(){returnclerk;    }publicvoidsetClerk(Clerk clerk){this.clerk = clerk;    }@Overridepublicvoidrun(){while(true) {            clerk.getProduct();        }    }}// 消费者classCustomerimplementsRunnable{privateClerk clerk;publicCustomer(){    }publicCustomer(Clerk clerk){this.clerk = clerk;    }publicClerkgetClerk(){returnclerk;    }publicvoidsetClerk(Clerk clerk){this.clerk = clerk;    }@OverridepublicStringtoString(){return"Customer [clerk="+ clerk +"]";    }@Overridepublicvoidrun(){while(true){            clerk.saleProduct();        }    }}

    相关文章

      网友评论

          本文标题:java核心技术-多线程基础

          本文链接:https://www.haomeiwen.com/subject/qcumqqtx.html