zoukankan      html  css  js  c++  java
  • 多线程和包

    一、多线程

    进程:正在进行中的程序(直译)

    线程:就是进程中控制程序执行的一个控制单元(执行路径)。

    一个进程中可以有多个执行路径,称为多线程。

    多线程的好处:解决了多个部分同时运行的问题。

    多线程的缺点:线程太多后效率低下。

    JVM运行的时候至少有两个线程:

    1.主线程,执行main函数

    2.负责垃圾回收

    创建新执行线程有两种方法:

    1.将类声明为Thread的子类,该子类应重写Thread类的run方法。接下来可以分配并启动该子类的实例。

    但是直接在主线程中调用线程类的run方法,并不能执行该线程,只相当于在主线程中new该线程类,仍然处于主线程内。要想创建并启动一个线程,在new该线程类后,用start()方法启动该线程。

    如:

    class Demo extends Thread
    {
    
        void run()
        {
         }
    }
    class Zu
    {
        public  static void main(String [] args)
        {
               Demo d=new Demo();
              d.start();
        }
    }

    线程的四种状态:

    有一种特殊的状态:就绪。具备了执行资格,但是还没有获取资源。

    CPU的执行资格:可以被CPU处理,在执行队列中排队。

    CPU的执行权:正在被CPU处理。

    所以,上图的运行状态为具备执行资格,同时具备执行权。冻结状态为不具备执行权,也不具备执行资格。如果具备了执行资格,但是沿未具备执行权,则为就绪状态。

    2.实现Runable接口

    在Runnable的子类中实现run方法,通过Thread类的创建线程对象,并将Runnable接口的子类对象作为Thread类线程对象构造函数的参数传入,最后调用线程对象的start()启动线程。

    如:

    class Demo implements Runnable
    {
        void run()
        {
         }
    }
    class Zu
    {
        public  static void main(String [] args)
        {
               Demo d=new Demo();
              Thread th=new Thread(d);
              th.start();
        }
    }

    实现Runnable接口的好处:

    1.将线程的任务从线程的子类中分离出来,进行单独的封装。按照面向对象的思想将任务封装成对象。

    2.避免了Java单继承的局限性。

    线程安全问题产生的原因:

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

    2.操作共享数据的代码有多条

    在Java中用同步代码块可以解决上述问题。

    synchronized(对象锁)

    {

         需要同步的代码;

    上述同步代码块中的对象是任意的,比如Object obj=new Object();中的obj

    同步的优点:解决了线程的安全问题

    同步的缺点:同步代码中有可能失去CPU执行权而等待,效率比较低。

    同步的前提:必须是多个线程使用同一个对象锁。

    对于一个函数也可以用同步锁。

    public synchronized void add(int num)

    {

       ……

    }

    同步函数的锁为函数所属的对象本身。

    建议使用同步块,少用同步函数。

    线程间通讯:

    等待唤醒机制:

    1.wait(); //让线程处于冻结状态,被wait的线程会被存储到线程池

    2.notify(); //唤醒线程池中的一个线程(任意)

    3.notifyall(); //唤醒线程池中的所有线程

    注意:上述方法必须定义在同步中,因为这些方法都是操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程。

    为什么操作线程的方法wait、notify、notifyall定义在了Object类中?因为这些方法就是监视器的方法,监视器其实就是锁。锁可以是任意的对象,任意对象的调用方式一定定义在Object类中。

    要注意的是 sleep和wait方法都会抛出异常,这些异常不能用throws向上抛,因为Runnable接口中未抛出异常,只能用try来处理异常。

    多生产者、多消费者的问题(烤鸭问题):

    package test;
    
    class Duck
    {
        private int num=0;
        Boolean flag=false;
        public void set()
        {
            synchronized (this) {
                while (flag)
                {
                    try
                    {
                        this.wait();
                    }
                    catch(InterruptedException e)
                    {}
                }
                num++;
                System.out.println("生产…………"+num);
                flag=true;
                notifyAll();
            }
        }
        
        public void get()
        {
            synchronized (this) {
                while (!flag)
                {
                    try
                    {
                        this.wait();
                    }
                    catch(InterruptedException e)
                    {}
                }
                System.out.println("消费………………"+num);
                flag=false;
                notifyAll();
            }
        }
    }
    
    class Producer implements Runnable{
        Duck duck;
        public Producer(Duck d) {
            this.duck=d;
        }
        @Override
        public void run() {
            while(true)
            {
                 duck.set();
            }
        }
    }
    
    class Consummer implements Runnable{
        Duck duck;
        public Consummer(Duck d) {
            this.duck=d;
        }
        @Override
        public void run() {
            while(true)
            {
              duck.get();
    
            }
        }
    }
    
    public class MyThread {
    
        public static void main(String[] args) {
            Duck duck=new Duck();
            Thread t0=new Thread(new Producer(duck));
            Thread t1=new Thread(new Producer(duck));
            Thread t2=new Thread(new Consummer(duck));
            Thread t3=new Thread(new Consummer(duck));
            t0.start();
            t1.start();
            t2.start();
            t3.start();
    
        }
    
    }

    if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。

    while判断标记,解决了线程获取执行权后,是否要运行!

    notify只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。

    notifyall解决了本方线程一定会唤醒对方线程。

    wait和sleep的区别:

    1.wait可以有时间参数,也可以没有

       sleep一定有时间参数

    2.wait释放CPU执行权,同时释放锁

       sleep释放CPU执行权,但是不释放锁

    在jdk1.5以后,将同步和锁封装成了对象,并将操作锁的方法定义到了该对象中,将隐式动作定义成了显示动作。常用lock接口对象代替同步代码块。

    Lock lock=new ReentrantLock();
    
    void show()
    
    {
    try
    { lock.lock(); ……
    }
    finally
    { lock.unlock();
    } }

    Lock替代Synchronized,而Condition替代对象锁。一个Lock,可用newCondition方法生成多个Condition对象,每个Condition对象对应不同的wait,notify,notifyAll。在Condition对象中wait,notify,notifyAll对应为await、signal、signalAll。

    例如上例的多生产者、多消费者的问题(烤鸭问题),用Lock接口写,代码如下:

    package test;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class Duck
    {
        private int num=0;
        Boolean flag=false;
        Lock lock=new ReentrantLock();
        Condition pro_lock=lock.newCondition();
        Condition con_lock=lock.newCondition();
        public void set()
        {
            lock.lock();
            while (flag)
            {
                try
                {
                    pro_lock.await();
                }
                catch(InterruptedException e)
                {}
            }
            num++;
            System.out.println("生产…………"+num);
            flag=true;
            con_lock.signal();
            lock.unlock();
        }
        
        public void get()
        {
            lock.lock();
            while (!flag)
            {
                try
                {
                    con_lock.await();
                }
                catch(InterruptedException e)
                {}
            }
            System.out.println("消费………………"+num);
            flag=false;
            pro_lock.signal();
            lock.unlock();
        }
    }
    
    class Producer implements Runnable{
        Duck duck;
        public Producer(Duck d) {
            this.duck=d;
        }
        @Override
        public void run() {
            while(true)
            {
                 duck.set();
            }
        }
    }
    
    class Consummer implements Runnable{
        Duck duck;
        public Consummer(Duck d) {
            this.duck=d;
        }
        @Override
        public void run() {
            while(true)
            {
              duck.get();
    
            }
        }
    }
    
    public class MyThread {
    
        public static void main(String[] args) {
            Duck duck=new Duck();
            Thread t0=new Thread(new Producer(duck));
            Thread t1=new Thread(new Producer(duck));
            Thread t2=new Thread(new Consummer(duck));
            Thread t3=new Thread(new Consummer(duck));
            t0.start();
            t1.start();
            t2.start();
            t3.start();
    
        }
    
    }

    停止线程的方法:

    1.stop方法

    2.run方法

       run方法一般都有循环,只要控制循环的次数就可以控制停止线程。

    中断线程:interrupt  强制取消线程的冻结状态,让线程具备CPU执行资格。但是强制中断会发生中断异常。

    线程对象的daemon方法,可以实现线程的前后台切换,当所有线程均为后台线程时,则程序结束。

    线程对象的join方法,可以中止当前线程,执行join方法的线程对象,执行完后,当前线程才能重新获得CPU执行权。

    Thread.yield方法可以暂停当前线程,继续执行其他的线程。

    二、包(package)

    注意点:

    1.对类文件进行分类管理

    2.给类提供多层命名空间

    3.写在程序文件的第一行

    4.类名的全称是:包名.类名

    5.包也是一种封装形式

    6.包在资源管理器中体现为文件夹

    对象的权限:

            public     protected    default    private

    同一个类中    ok      ok       ok      ok

    同一个包中    ok      ok       ok      ok

    子类中      ok      ok

    不同包中     ok

  • 相关阅读:
    OpenMP vs WinSxS
    JIT, dynarec and binary translation
    VC++2010 bug
    控制和释放共享内存块
    分配和释放信号量
    代码列表5.1 (shm.c) 尝试共享内存
    绑定和脱离
    信号量
    每个字段动态添加一个随机数
    最近做了一个红底鞋类电商网站
  • 原文地址:https://www.cnblogs.com/jsddj/p/7635056.html
Copyright © 2011-2022 走看看