黑马程序员——多线程-整理笔记

                                                  ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


多线程的概念

          首先介绍什么是进程:进程是正在执行的程序,每个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元

          线程是其实就是进程中一个独立的控制单元,线程在控制着执行,一个进程至少有一个线程。

线程和进程的不同:

        ①地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;;
        ②资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源;
        ③线程是处理器调度的基本单位,但进程不是;

线程和进程的优缺点:

       线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

多线程的好处:

      在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(,因此创建多个线程去执行一些任务会比创建多个进程更好。

多线程的特点:

       随机性,谁先抢到谁先执行,执行时间有CPU决定。

线程创建的两种方法:

     方法一:继承Thread类

           步骤:

                 ①定义类继承Thread类;
                 ②复写Thread类中的run方法,将要让线程运行的代码都存储到run方法中;
                 ③通过创建Thread类的子类对象,创建线程对象;
                 ④调用线程的start方法,开启线程,并执行run方法  ;

    继承Thread类的代码示例:

public class Demo extends Thread {
	public void run() {
		for (int i = 0; i < 60; i++)
			System.out.println("Demo run" + i);
	}
}

class ThreadDemo {
	public static void main(String[] args) {
		Demo d = new Demo();// 创建一个线程
		d.start(); // start启动线程

		for (int i = 0; i < 60; i++)
			System.out.println("main   " + i);
	}
}

        方法二:实现Runnable接口

              步骤:

                ①定义类实现Runnable接口 
                ②覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中;
                ③通过Thread类建立线程对象;
                ④将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;
                ⑤调用Thread类的start方法开启线程;

       实现Runnable接口的代码示例


public class MyThread {


<span style="white-space:pre">	</span>public static void main(String[] args) {
<span style="white-space:pre">		</span>Ticket t = new Ticket();
<span style="white-space:pre">		</span>// 线程开始
<span style="white-space:pre">		</span>new Thread(t).start();
<span style="white-space:pre">		</span>new Thread(t).start();
<span style="white-space:pre">	</span>}
}


// 实现Runnable接口
class Ticket implements Runnable {


<span style="white-space:pre">	</span>private int t = 100;


<span style="white-space:pre">	</span>// 覆run方法
<span style="white-space:pre">	</span>public void run() {
<span style="white-space:pre">		</span>while (true) {
<span style="white-space:pre">			</span>if (t > 0) {
<span style="white-space:pre">				</span>System.out.println(Thread.currentThread().getName() + ":::"
<span style="white-space:pre">						</span>+ t--);
<span style="white-space:pre">			</span>}


<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}


}


实现接口方式和继承类方式的区别?

  实现Runnable接口可以避免单继承的局限性,最常使用实现方式。

  区别:继承Thread:线程代码存放Thread子类run方法中。实现Runnable:线程代码存在接口的子类run方法中。


线程一共分为5种状态:
        ①被创建new 
        ②sleep 睡眠状态,(time)给他一个参数,时间一到就会恢复到临时状态;
        ③wait(等待), 直到被 notify(通知)才会回到临时状态;
        ④stop(消亡状态)  run方法结束了线程也会消亡;
        ⑤临时状态或者被叫(阻塞状态),它具备运行资格,但是没有执行权)。线程被start之后 在等待CPU的执行权;


线程状态图



线程安全:

     线程安全产生的原因:多个线程共同操作同一个共享数据;有多条语句对共享数据进行计算,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

     解决方案:同步代码块或同步函数。

       同步代码块的格式

           synchronized(对象) {  //任意对象都可以。这个对象就是锁。
                                  需要被同步的代码;
                               }

      同步函数: 所谓的同步函数就是在函数的返回值前面加一个synchronized关键字就是同步函数了。

         同步代码块的锁是参数对象,

         同步函数的锁是this,

         静态同步函数的锁是该类的字节码文件对象。

         在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。

    同步线程的前提:

          ①必须要有两个或者两个以上的线程。
          ②必须是多个线程使用同一个锁。
          ③必须保证同步中只有一个线程在运行。
            好处:解决了多线程的安全问题。
            弊端:多个线程需要判断锁,较为消耗资源。

单例设计模式的懒汉式:

           对象是方法被调用时才初始化,也叫对象的延时加载 。下例中Single类进入内存,对象还没有存在,只有调用了getInstance方法时,才建立对象的懒汉式;

           懒汉式的特点就是延迟加载, 不过还是存在问题,如果多线程时会出现安全问题,可以加同步代码块解决问题,加同步时用的锁必须是该类所属的字节码文件对象。

  懒汉式代码示例:

/* 
 * 延迟加载模式——懒汉式 
 * 加入同步机制,解决安全问题。但是却带来了效率降低。 
 * 为了效率问题,通过双重判断的形式解决。 
 */  
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 Test implements Runnable  
{  
    private boolean flag;  
    Test (boolean flag)  
    {  
        this.flag=flag;  
    }  
  
    public void run ()  
    {  
        if (flag)  
        {  
            synchronized(MyLock.locka)//a锁  
            {  
                System.out.println("if locka");  
                synchronized(MyLock.lockb)//b锁  
                {  
                    System.out.println("if lockb");  
                }  
            }  
        }  
        else   
        {  
            synchronized(MyLock.lockb)//b锁  
            {  
                System.out.println("else lockb");  
                synchronized(MyLock.locka)//a锁  
                {  
                    System.out.println("else locka");  
                }  
            }  
        }  
    }  
}  
//定义两种锁的类
class MyLock  
{  
    static Object locka = new Object();  
    static Object lockb = new Object();  
}  
  
class  DeadLockDemo  
{  
    public static void main(String[] args)   
    {  
        Thread t1 = new Thread(new Test(true));  
        Thread t2 = new Thread(new Test(false));  
  
        t1.start();  
        t2.start();  
    }  
}  


                                                  ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值