博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程和包
阅读量:4968 次
发布时间:2019-06-12

本文共 6360 字,大约阅读时间需要 21 分钟。

一、多线程

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

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

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

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

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

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

 

转载于:https://www.cnblogs.com/jsddj/p/7635056.html

你可能感兴趣的文章
springIOC第一个课堂案例的实现
查看>>
求输入成绩的平均分
查看>>
php PDO (转载)
查看>>
wordpress自动截取文章摘要代码
查看>>
[置顶] 一名优秀的程序设计师是如何管理知识的?
查看>>
scanf和gets
查看>>
highcharts 图表实例
查看>>
ubuntu下如何查看用户登录及系统授权相关信息
查看>>
秋季学期学习总结
查看>>
SpringBoot 优化内嵌的Tomcat
查看>>
【LaTeX】E喵的LaTeX新手入门教程(1)准备篇
查看>>
highcharts曲线图
查看>>
extjs动态改变样式
查看>>
PL/SQL Developer 查询的数据有乱码或者where 字段名=字段值 查不出来数据
查看>>
宏定义
查看>>
笔记:git基本操作
查看>>
生成php所需要的APNS Service pem证书的步骤
查看>>
JavaWeb之JSON
查看>>
HOT SUMMER 每天都是不一样,积极的去感受生活 C#关闭IE相应的窗口 .
查看>>
windows平台上编译mongdb-cxx-driver
查看>>