多线程和包

一、多线程

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

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

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

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

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

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

 

posted @ 2017-10-07 16:29  jsddj  阅读(385)  评论(0编辑  收藏  举报