多线程

来源:互联网 发布:查询重复数据的sql 编辑:程序博客网 时间:2024/05/17 04:54

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

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

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

一个进程之中至少有一个线程。

 

开启多个线程是为了同时运行多部分代码。

每一个线程都有自己运行的内容,这个内容可以成为线程要执行的任务。

 

jvm启动时就启动了多个线程,至少有两个线程可以分析出来。

1,执行main函数的线程。

         该线程的任务代码都定义在main函数中。

2,负责垃圾回收的线程。

 

如何创建一个线程?

创建线程方式一:继承thread类

步骤:

1,定义一个类继承Thread类。

2,覆盖Thread类中的run方法。将线程运行代码封装到run方法中。

3,直接创建Thread的子类对象。

4,调用start方法开启线程并调用线程的run方法执行。

 

 

 

创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。

运行的指定代码就是这个执行路径的任务。

 

jvm创建的主线程的任务都定义在了主函数中。

自定义的线程任务在哪?

Thread类用于描述线程,线程是需要任务的。所以Thread类也对任务描述。

这个任务就是通过Thread类中的run方法来体现。也就是说,run方法就是封装自定义线程运行任务的函数。

 

run方法中定义就是线程要运行的任务代码。

开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法。将运行的代码定义在run方法中即可。

 

 

 

可以通过Thread的getName获取线程的名称   Thread-编号(从0开始)

Thread.currentThread()可以得到当前运行的线程的对象。

Thread的子类在创建对象的时候就默认给线程起了一个名字(不管该线程是否会运行)Thread-编号(从0开始)。

Thread默认有参的构造函数可以给创建的线程命名。

 

创建线程方式二:实现Runnable接口。

如果某个类已经有了父类,为了扩展类的功能,让其中的内容可以作为线程的任务执行。通过接口的形式完成。

步骤:

1,定义类实现Runnable接口。

2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。

3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。

所以要在线程对象创建时就必须明确要运行的任务。

4,调用线程对象的start方法开启线程。

 

 

线程的状态






实现Runnable接口的好处:

1,将线程的任务从线程的子类中分离出来,进行了单独的封装。

         按照面向对象的思想将任务封装成对象。

2,避免了java单继承的局限性。

所以,创建线程的第二种方式较为常用。

 

 

 

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

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

2,操作共享数据的线程代码有多条。

当一个现场呢狗仔执行操作共享数据的多条代码过程中,其他的线程参与了运算。

就会导致线程安全问题的产生。

解决思路:

就是讲多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的。

必须要当线程把这些代码都执行完毕后,其他线程才可以参与运算。

 

第一种方法同步代码块就可以解决这个问题。

同步代码块格式:

synchronized(对象)对象相当于标志位,同步锁

{

         需要被同步的代码块;

}

对象可以是Object  obj =new Object();

 

同步的好处:解决了线程的安全问题。

同步的弊端:想回降低了效率,因为同步外的线程都会判断同步锁。

 

同步的前提:必须有多个线程并使用同一个锁。也就是每个线程要共享一个Object对象。

 

第二种方法同步函数

同步函数的同步锁就是那个对象调用这个同步函数就把这个对象作为同步锁。也就是this

 

同步函数和同步代码块的区别:

同步函数的锁是固定的this。

同步代码块的锁是任意的对象。

建议使用同步代码块。

 

 

静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取(this. getClass),也可以用当前类名.class表示。

 

死锁常见场景一:同步的嵌套

 

class MyLock{

public static final Object locka =newObject();

public static final Object lockb =newObject();

}

 

 

class Test implements Runnable

{

         privateboolean flag;

         Test(booleanflag){

                   this.flag=flag;

         }

         publicvoid run(){

                   if(flag){

                            while(true)

                            synchronized(MyLock.locka){

                                     System.out.println(Thread.currentThread().getName()+"if  locka..");

                                     synchronized(MyLock.lockb){

                                     System.out.println(Thread.currentThread().getName()+"else  lockb..");                               

                                     }

                            }

                   }else{

                            while(true)

                            synchronized(MyLock.lockb){

                                     System.out.println(Thread.currentThread().getName()+"if  lockb..");

                                     synchronized(MyLock.locka){

                                     System.out.println(Thread.currentThread().getName()+"else  locka..");                               

                                     }

                            }

                   }

         }

}

class DeadLockTest{

         publicstatic void main(String [] args){

                   Testa=new Test(true);

                   Testb=new Test(false);//这里有点小问题。不过不影响产生死锁。

                   Threadt1=new Thread(a);

                   Threadt2=new Thread(b);

                   t1.start();

                   t2.start();

         }

}

 

 

线程间通讯:多个线程在处理同一资源,但是任务却不同。

 

等待唤醒机制。

涉及的方法:

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

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

3,notifyAll();唤醒线程池中的所有线程。
锁==监视器

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

为什么操作线程的方法wait() notify() notify()定义在了Object类中。

因为这些方法是监视器的方法。监视器其实就是锁。

锁可以是任意的对象,任意的对象调用的方法一定定义在Object类中。

 

 

生产者消费者问题

 

 

多生产者多消费者问题。视频27

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

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

 

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

notifyAll解决了,本方线程一定会唤醒对方线程的问题。

 

 

生产者消费者问题解决---------jdk1.5新特性   lock   视频30

同步代码块,对于锁的操作时隐式的。

jdk1.5以后将同步和锁封装成了对象。并讲操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。

 

 

Lock接口:出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成显示锁操作。

同时更为灵活。可以一个锁加上多组监视器。

lock():获取锁

unlock():释放锁,通常需要定义finally代码块中。

 

Condition接口:出现替代Object中的wait  notify notifyAll方法。

将这些监视器方法单独进行了封装,变成Condition监视器对象。

可以任意进行组合

await()

signal()

signalAll()

 

 

wait 和sleep的区别

1,wait可以指定时间也可以不指定。

sleep必须指定时间。

2,在同步中时,对于cpu的执行权和锁的处理不同。

wait :释放执行权,释放锁。

sleep:释放执行权,不释放锁。

 

 

停止线程:

1.stop方法。已经过时

 

2,run方法结束

怎样控制线程的任务结束呢?

任务中都会有循环结构,只要控制住循环就可以结束任务。控制循环通常用定义标记完成。

 

但是如果线程处于了冻结状态,无法读取标记,该如何结束?

可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格,但是强制动作会发生了InterruptedException,记得要处理。

 

 

setDaemon()将线程标记为守护线程或标记线程(后台线程)。

和前台线程对比:开启运行都一样,

前台线程必须要手动结束,

后台线程如果前台线程都结束了,后台线程也随之结束,无论是否处于冻结状态。

 

 

join() 方法线程对象.join(),执行这句话的线程等待该线程执行结束。

 

yield() 方法暂停,暂时释放控制权。









0 0
原创粉丝点击