黑马程序员-多线程

来源:互联网 发布:网络神助攻什么意思 编辑:程序博客网 时间:2024/05/17 07:55

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

创建线程的两种方式:
继承Thread类,用代码体现:

public class MyThread extends Thread
   {
    public void run()
    {
     for(int x=0; x<100; x++)
     {
      System.out.println(getName()+"---"+"hello"+x);
     }
    }
   }

   public MyThreadTest
   {
    public static void main(String[] args)
    {
     MyThread my1 = new MyThread();
     MyThread my2 = new MyThread();

     my1.start();
     my2.start();
    }
   }

实现Runnable接口
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法

用代码体现
   public class MyRunnable implements Runnable
   {
    public void run()
    {
     for(int x=0; x<100; x++)
     {
      System.out.println(Thread.currentThread().getName

()+"---"+"hello"+x);
     }
    }
   }
   
   public class MyRunnableTest
   {
    public static void main(String[] args)
    {
     MyRunnable my = new MyRunnable();

     Thread t1 = new Thread(my);
     Thread t2 = new Thread(my);

     t1.start();
     t2.start();
    }
   }

注意:复写run方法的目的是将自定义的代码存储在run方法中,让线程运行
将线程要运行的代码存放在该run方法中
3.通过Thread类建立线程对象
4.通过Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
为什么呢?因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要
让线程去指定指定对象的run方法。就必须明确该run方法所属对象
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
start方法有两个作用:
一是启动线程,二是调用run方法
线程有自己默认的名称:
格式:Thread-编号,编号从0开始。
static Thread currentThread():获取当前线程对象
getName():获取线程名称
设置线程名称:setName或者构造函数
实现方式和继承方式有什么区别呢?
实现方式好处是:避免了单继承的局限性,建议使用它
继承方式:线程代码存放在Thread子类run()方法中
实现方式:存在接口的子类的run方法中
线程安全:
我们先来看段代码:
package thread;

public class TicketsRunnable implements Runnable
{
private int tickets=200;
public void run()
{
  while(true)
  {
   if(tickets>0)
   {
    System.out.println(Thread.currentThread().getName()+"....sale:"+tick--);

   }
  }
}

}

package thread;

public class TicketDemo
{
public static void main(String[] args)
{
  TicketsRunnable t=new TicketsRunnable();
 
  Thread t1=new Thread(t);
  Thread t2=new Thread(t);
  Thread t3=new Thread(t);
  Thread t4=new Thread(t);

  t1.start();
  t2.start();
  t3.start();
  t4.start();
 
}

}
分析:
在判断中,假如tickets大于1,满足条件,所以Thread-1准备去执行,但是有可能出现一种情况,就是Thread-1
刚判断完正要执行的时候,但是被线程Thread-2给抢去了,而Thread-2正准备要执行,但是被Thread-3抢去了,Thread-3正要准备
去执行的时候,但是被Thread-4给抢去了,这四个全部挂着,但是它们都有执行的资格,假设当Thread-2,Thread-3,Thread-4全部挂着的时候
切换到Thread-1,它就买到1号票了,其他的线程因为不要再判断了,所以他们买到的票tick--就分别是0,-1,-2张错票,这显然不对,所以这里线程不安全。
造成这个问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,
另一个线程参与进来执行,导致共享数据的错误
解决办法:
对多条操作共享数据的语句,只能让一个都执行完,在执行过程中,其他线程不能参与进来
Java对于多线程安全问题使用同步代码块来解决:
synchronized(对象)
{
     需要被同步的代码    
}
哪些语句正在操作共享数据,那哪些代码块就需要被同步,如下代码所示:
package thread;

public class TicketetsRunnable implements Runnable
{
private int tickets=1000;
Object obj=new Object();
public void run()
{
  while(true)
  {
   synchronized(obj)
   {
    if(tick>0)
    {
     try {
      Thread.sleep(100);
     } catch (InterruptedException e) {
     
     }
     System.out.println(Thread.currentThread().getName()+"....sale:"+(tickets--));
         }
    }
  }
}

}
同步代码块的原理:
用锁的思想去理解
对象如同锁,持有锁的线程可以在同步中执行,没有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:
1.必须要有两个或者两个以上的线程
2.必须是多个线程使用同一个锁
必须保证同步中有一个线程在运行就可以了
弊端:多个线程需要判断锁,较为消耗资源
多线程-同步函数:
如何同步?
1.明确哪些代码是多线程运行代码
2.明确共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的
在函数的返回值前面加上synchronized,它就变成同步函数了
同步函数用的是哪一个锁呢?
函数需要被对象调用,函数都有一个所属对象的引用,那就是this
所以同步函数使用的锁是this。
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不是this。因为静态方法中也不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象,所以它的锁可以用字节码文件对象
格式:类名.class。
该对象的类型是Class.
单例设计模式:
饿汉式:
class Single
{
    private static final Single s=new Single();
    private Single(){}
    public static Single getInstance()
{
      return s;
  }
}
懒汉式:
class Single
{
    private static final Single s=null;
    private Single(){}
    public static synchronized Single getInstance()
    {
         if(s==null)
               s=new Single();
         return s;
}

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


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

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

懒汉式面试经常问到。
第二种方式效率提高了。
单例设计模式涉及的知识点必须要会!。
线程间通信,其实就是多个线程在操作同一个资源。

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

0 0
原创粉丝点击