黑马程序员_多线程

一、基本概念

进程:正在进行的程序。

线程:就是进程中一个执行单元或执行情景或执行路径,负责进程中程序执行的控制单元 。

进程和线程的关系:   一个进程中至少要有一个线程,进程在执行过程中拥有一个独立的内存单元,而多个线程共享内存。

        多线程:当一个进程中线程有多个时,就是多线程。

       多线程解决的问题:可以让多部分代码同时执行。

二、创建线程的两种方式

1、方式一:继承Thread类,覆盖run方法。

  步骤:
(1)、定义类继承Thread类。
2)、覆盖Thread类中的run方法。
3)、创建Thread类的子类对象创建线程对象。
4)、调用线程对象的start()方法开启线程。 

代码体现:

public static void main(String[] args) {
demo d1=new demo();
demo d2=new demo();
d1.start();
d2.start();
for(int y=0;y<5;y++){
System.out.println(y+"......."+Thread.currentThread());
}
}
}
class demo extends Thread{
public void run(){
show();
}
public void show(){
for(int x=0;x<10;x++){
System.out.println(x+"......."+Thread.currentThread()+this.getName());
}
}
}


2、方式二:实现Runnable接口。

   步骤:
(1)、定义一个类实现Runnable接口。
(2)、覆盖Runnable接口中的run方法。将线程要运行的代码存储到run方法中。 
(3)、创建该接口的子类对象。
(4)、通过Thread类进行线程的创建,并将Runnable接口的子类对象作为Thread类的构造函数的实参进行传递。
(5)、调用Thread类中的start()方法开启线程。 


代码体现:

public static void main(String[] args) {

demo1 d1=new demo1();
Thread t1=new Thread(d1);
Thread t2=new Thread(d1);
Thread t3=new Thread(d1);
Thread t4=new Thread(d1);
t1.start();
t2.start();
t3.start();
t4.start();
for(int x=0;x<6;x++){
System.out.println(x+"*********"+Thread.currentThread().getName());
}
}
}
class demo1 implements  Runnable{
public void run(){
show();
}
public void show(){
for(int x=0;x<10;x++){
System.out.println(x+"......."+Thread.currentThread());
}
}
}


Runnable接口的由来其实就是将线程的任务进行对象的封装。

将线程任务封装成对象后,通过Runnable接口可以降低和Thread对象的耦合性。

如果是继承Thread类,覆盖run方法这种情况,
Thread的子类即封装了线程的任务,而且自身还是一个线程对象
这就是任务和对象耦合性过高。

       两种方法区别:

       1、实现Runnable接口避免了单继承的局限性。

       2、继承Thread类线程代码存放在Thread子类的run方法中,  实现Runnable接口线程代码存放在接口的子类的run方法中。

总结:在定义线程时,建议使用实现Runnable接口,因为几乎所有多线程都可以使用这种方式实现。

3、start()方法和run()方法的区别

调用run方法,仅仅是一般对象在调用对象中的方法,并没有开启线程,
还有主线程来完成的run方法的运行。
调用start方法,是开启了一个线程(一个新的执行路径。)
这个线程去执行了run方法。

    总结:调用run方法,只是调用对象中的run()方法。

调用start()方法,开启线程并调用run()方法。


三、多线程的安全问题

安全问题产生的原因:

(1)、 多个线程在操作共享数据。

(2)、操作共享数据的代码有多条,一个线程在执行多条操作共享数据的过程中,其他线程参数与了运算,这时就会发生安全问题。


解决方案:只要保证一个线程在执行多条操作共享数据的语句时,其他线程不能参与运算即可。当该线程都执行完后,其他线程才可以执行这些语句。


四、多线程安全问题的解决:同步

1、同步:是用来解决多线程的安全问题的,在多线程中,同步能控制对共享数据的访问。如果没有同步,当一个线程在修改一个共享数据时,而另外一个线程正在使用或      者更新同一个共享数据,这样容易导致程序出现错误的结果。

2、同步的原理:其实就是将需要同步的代码进行封装,并在该代码上加了一个锁。

3、同步的前提:
   必须要保证在同步中有多个线程。因为同步中只有一个线程该同步是没有意义。
必须要保证多个线程在同步中使用的是同一个锁。

4、锁  : 

(1)、锁就是一个对象。

(2)、锁的作用是保证线程同步,解决线程安全问题。

       (3)、持有锁的线程可以在同步中执行,没有锁的线程即使获得cpu执行权,也进不去。

5、同步的利弊:

       利:同步解决了多线程的安全问题。

       弊:同步需要判断锁,比较消耗资源,同步嵌套会出现死锁。

6、同步的三种形式:

(1)、同步代码块:同步代码块的锁是任意对象。

synchronized(对象){

需要同步语句

}

(2)、同步函数:

同步函数的锁是this。

修饰词synchronized 返回值类型 函数名(参数列表)

       {

               需同步的代码;

       }

(3)、静态同步函数

静态同步函数就是将静态方法进行同步。静态同步函数的锁是该方法所在的类的字节码文件对象,即类名·class文件。格式和同步函数差不多,只是锁不同而已。


7、线程间通信:多个线程在处理同一个资源。 但是处理的动作(线程的任务)却不相同。

wait():等待,将正在执行的线程释放其执行资格和执行权,并存储到线程池中。
notify():唤醒,唤醒线程池中被wait的线程,一次唤醒一个,而且是任意的。
notifyAll():唤醒全部,可以将线程池中的所有wait线程都唤醒。

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。


       JDK1.5后出现的新的接口和类:

       将同步synchonized替换成了显示的Lock操作,将Object中的wait、notify、notifyAll替换成了Condition对象。该对象可以Lock锁进行获取。

Condition中提供了监视器的方法:await(),signal(),signalAll()。


五、多线程的单例设计模式

饿汉式。

class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}


在被多线程并发访问时,会出现多线程安全问题。为了解决,加入了同步,虽然安全问题解决了,但是性能降低了。
为了解决安全问题,而且还可以提高性能,就使用了同步代码块,可以通过双重判断的形式来完成这个过程。 


懒汉式(单例延迟的加载模式)

class Single
{
private static Single s = null;
private Single(){}
public static  Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}

class SingleDemo 
{
public static void main(String[] args) 
{
System.out.println("Hello World!");
}
}


六、其他

1、停止线程:停止线程就是让run方法结束。

(1)、stop():有固有的不安全性。已过时。

(2)、结束run方法,只要控制住run中的循环即可,控制循环通常需要通过标记来完成。

2、守护线程(后台线程)

       setDaemon():将该线程标记为守护线程或者用户线程。当主线程结束,守护线程自动结束。

守护线程的特点:

       守护线程开启后和前台线程共同抢夺cpu的执行权,开启、运行两者都没区别,但结束时有区别,当所有前台线程都结束后,守护线程会自动结束。

3、加入线程

     join():等待该线程终止。

       join(long millis):等待该线程终止的时间最长为millis毫秒。

       join方法会抛异常:throws InterruptedException

特点:当A线程执行到B线程的join方法时,A就会等待B都执行完,A才会执行。

       作用:join方法可以用来临时加入线程执行。


4、wait()和sleep()方法的异同点:

(1)、这两个方法来自不同的类,sleep()来自Thread类,和wait()来自Object类。

      (2)、sleep()是Thread的静态类方法,谁调用的谁就处于睡眠状态,即使在a线程里调用了b的sleep方法,实际上还是a睡眠,要让b线程处于睡眠状态还是要在b的代码中调用sleep方法。而wait方法是Object类的非静态方法。

      (3)、sleep()释放资源不释放锁,而wait()释放资源释放锁。

       (4)、使用范围:wait()、notify()和notifyAll()只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。



 

      

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值