java 中多线程的理解

jvm启动其实不止一个线程,它只少启动两个线,因为有一个是GC,另一个启动的程序。

意义在于提高效率。线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。

线程的创建方式:
第一种方式   1、继承Thread  2、重写run方法,3、调用start()方法,这时jvm就会调用run方法。   多线程的随机性,who抢cpu到who执行,至于执行多长时间,cpu说了算。当然也可以人工去干预
为什么要重写run方法呢:线程有启动,有运行,运行什么呢,当然是运行run方法中的代码。
如果直接这样写
Thread thead=new Thead();   thead.start() ; 这样就没有运行的代码,没有意义,所以要重写run方法

如下案列面试经常会碰到

package cn.itcast.day5.thread;

public class BxdLessonOneThreadDemo {  

public static void main(String[] args)  {

  Demo demo = new Demo();

  //demo.start();  //如果这里不调用的话说明子线程没有运行,而只是运行一个普通的方法run(),所以这代码无论执行多少次都一样的结果。

  demo.run(); //这样是普通调用方法,与线程没有关系。

  for (int i = 0; i < 600; i++)   {   

 System.out.println("main hello world--------" + i);

  }  } }

class Demo extends Thread {  

@Override  public void run()  {  

 for (int i = 0; i < 600; i++)   {   

 System.out.println("sub:demo run-----------" + i);

  }  } }

第二种方式:1,实现runnable接口,重写runnable run方法,通过thread建立子线程,然后再start()   

开发中建议用这个,更面向对象,出时解决了java的单继承,而用接口来实现。

package cn.itcast.day5.thread;

/**
 * 多窗口卖票窗口体现多线程
 * 
 * @author Administrator
 * 
 */
public class MoreWindowSaleTicket
{
    public static void main(String[] args)
    {

        // SaleTicket1();

        SaleTicket();
    }

    private static void SaleTicket1()
    {
        Ticket ticket0 = new Ticket();
        Ticket ticket1 = new Ticket();
        Ticket ticket2 = new Ticket();
        Ticket ticket3 = new Ticket();
        Ticket ticket4 = new Ticket();

        ticket0.start();
        ticket1.start();
        ticket2.start();
        ticket3.start();
        ticket4.start();
    }

    private static void SaleTicket()
    {
        TicketRunable ticketRunable = new TicketRunable();
        Thread thread1 = new Thread(ticketRunable);
        Thread thread2 = new Thread(ticketRunable);
        Thread thread3 = new Thread(ticketRunable);
        Thread thread4 = new Thread(ticketRunable);

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

// 第一种方式
class Ticket extends Thread
{
    private static int count = 100;// 这里定义为静态也可以解决问题,但我们不这样这定义,一般会用另一种试,请参考SaleTicket():

    public void run()
    {
        while (count > 0)
        {
            System.out.println(Thread.currentThread().getName() + "-------" + count--);
        }
    }
}

// 第二种方式
class TicketRunable implements Runnable
{
    private static int count = 1000;

    @Override
    public void run()
    {
        while (true) // 这里会有个问题,如果count=1时.但cpu又执行另一个线程时可能导致count--=0
        {
            synchronized (TicketRunable.class) //线程同步,共享资源部分加上同步,它做的事情是让别人等待,它进来,然后别人进来,它等待,
            //这样一直执行操作,至到完成。TicketRunable.class也称为锁,进来出去就得用这把锁。跟火车上的WC一样,有人,无人,这是标准我锁。呵呵。
            //同步的前提是:必须是两个或是两个以上的线程才会加上同步锁,必须是多个线程使用同一个锁.
            //线程同步解决了线程的安全,但也有个不好的地方,就是耗资源,因为每次都要去检查锁。
            {
                if (count > 0)
                {
                    try
                    {
                        Thread.sleep(100);// 加上这个条件就有可能得到-1 0 -2
                                            // 张票,所以得考虑线程的安全性问题,得到这些票的原因是
                        // 当多条语句在操作同一个线程共享数据时,一个线程对多条语句执行了一部分,还没有执行完,而另一个线程参与进来,共享数据就会被修改掉,这
                        // 就是线程的安全问题。
                        // 解决办法:多条语句共享数据的语句,让它执行完再执行另一个线程,其它线程不参与执行,虽然它已取得执行权,就是同步代表块
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "-------:" + count--);
                }
            }
        }
    }
}
多线程卖票的安例

 

如何找到多线程的问题

1、明确哪些代码是多线程运行的代码,2、明确哪些是共享数据,3、明确多线程运行代码中哪些语句是操作共享数据的。

 

同步函数与同步代码,可以用同步函数修改下上面的卖票案例
只要把同步代码重写一个同步方法就可以了 但问题又来了。同步一定要锁,但它的锁呢,如果函数被那个调用就是那个对象的字符码,也就是this.  所以得出结果一个同步函数中它的锁就是this

 1 package cn.itcast.day5.thread;
 2 
 3 /**
 4  * 多窗口卖票窗口体现多线程
 5  * 
 6  * @author Administrator
 7  * 
 8  */
 9 public class MoreWindowSaleTicketSynchronizedFunction
10 {
11     public static void main(String[] args)
12     {
13         SaleTicket();
14     }
15 
16     private static void SaleTicket()
17     {
18         TicketRunable1 ticketRunable = new TicketRunable1();
19         Thread thread1 = new Thread(ticketRunable);
20         Thread thread2 = new Thread(ticketRunable);
21         Thread thread3 = new Thread(ticketRunable);
22         Thread thread4 = new Thread(ticketRunable);
23 
24         thread1.start();
25         thread2.start();
26         thread3.start();
27         thread4.start();
28     }
29 }
30 
31 // 第二种方式
32 class TicketRunable1 implements Runnable
33 {
34     private static int count = 1000;
35 
36     @Override
37     public void run()
38     {
39         while (true) // 这里会有个问题,如果count=1时.但cpu又执行另一个线程时可能导致count--=0
40         {
41             ShareSources();
42         }
43     }
44 
45     private synchronized void ShareSources() // 因为这部分是多线程操作的共享资源
46     // 由于同步都要用到锁,它的锁就是调用它的对象的字符码,也就是this
47     {
48         if (count > 0)
49         {
50             try
51             {
52                 Thread.sleep(100);// 加上这个条件就有可能得到-1 0 -2
53                                     // 张票,所以得考虑线程的安全性问题,得到这些票的原因是
54                 // 当多条语句在操作同一个线程共享数据时,一个线程对多条语句执行了一部分,还没有执行完,而另一个线程参与进来,共享数据就会被修改掉,这
55                 // 就是线程的安全问题。
56                 // 解决办法:多条语句共享数据的语句,让它执行完再执行另一个线程,其它线程不参与执行,虽然它已取得执行权,就是同步代表块
57             }
58             catch (InterruptedException e)
59             {
60                 e.printStackTrace();
61             }
62             System.out.println(Thread.currentThread().getName() + "-------:" + count--);
63         }
64     }
65 }
同步函数实现多窗口卖票

 验证同步函数的锁是this(不能用static修饭)

package cn.itcast.day5.thread;

/*
 * 用同步函数与同步代码来验证同步函数中的锁是this(调用这个访求的对象)
 */
public class UseDemoCheckThreadFunctionKeyIsKey
{
    public static void main(String[] args)
    {
        Ticket1 ticket1 = new Ticket1();
        Thread thread = new Thread(ticket1);

        Thread thread1 = new Thread(ticket1);
        thread.start();
        ticket1.flag = false;
        thread1.start();
    }
}

class Ticket1 implements Runnable
{
    int count = 100;
    boolean flag = true;
    //Object object = new Object();
    @Override
    public void run()
    {
        if (flag)
        {
            while (true)
            {
                synchronized (this) //这里的this就是这个对象,而同步函数中的也是this
                {

                    if (count > 0)
                    {
                        try
                        {
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName() + ":" + count--);
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
            }
        } else
        {
            while (true)
            {
                show();
            }
        }
    }

    public synchronized void show()
    {
        if (count > 0)
        {
            try
            {
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName() + ":" + count--);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
}
同步函数的锁是this

 那静态方法中的锁是什么呢 就是那个类的字节码对象

package cn.itcast.day5.thread;

/*
 * 用同步函数与同步代码来验证同步函数中的锁是this(调用这个访求的对象)
 */
public class UseDemoCheckThreadFunctionKeyIsKey
{
    public static void main(String[] args)
    {
        Ticket1 ticket1 = new Ticket1();
        Thread thread = new Thread(ticket1);
        Thread thread1 = new Thread(ticket1);
        thread.start();
        ticket1.flag = false;
        thread1.start();
    }
}

class Ticket1 implements Runnable
{
    static int count = 100;
    boolean flag = true;
    Object object = new Object();
    @Override
    public void run()
    {
        if (flag)
        {
            while (true)
            {
                synchronized (Ticket1.class) //由于是同步方法是静态的,那它的锁的是这个方法所以对象的字节码对象,而这里不能用this,
                //因为静态中没有this,所以要用字节码才能线程安全。
                {

                    if (count > 0)
                    {
                        try
                        {
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName() + ":" + count--);
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
            }
        } else
        {
            while (true)
            {
                show();
            }
        }
    }

    public static synchronized void show()
    {
        if (count > 0)
        {
            try
            {
                Thread.sleep(10);
                System.out.println(Thread.currentThread().getName() + ":" + count--);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
}
静态方法的中多线程

 由于单例中懒汉式在多线程中使用同步锁就会又程序效率降低,又一个好办法可以解决这个问题如下

// 延迟加载的同步懒汉式写法
class Single
{
    private static Single single = null;

    private Single()
    {
    }

    public static Single getInstance()
    {
        if (single == null)
        {
            synchronized (Single.class) // 线程A进来,休息一会,其它线程进不来,等它实例化后其它线和进来,发现single不为空了。
            // 就是不会在实例化了,这样就提高了程序的运行率, 所以面试一般会面试这个,便实际开发中最好用饿汉式如下
            {
                if (single == null)
                {
                    single = new Single();
                }
            }
        }
        return single;
    }
}

//饿汉式
class SingleHugry
{
    private static SingleHugry singleHugry = null;

    private SingleHugry()
    {
        singleHugry = new SingleHugry();
    }

    public static SingleHugry getInstance()
    {
        return singleHugry;
    }
}
延迟加载的同步懒汉式写法

死锁:A有自己的锁,但要进B内,B有锁,要进A内,这样就会造成死锁:一般出现在多线程同步中,多个锁出现在一个方法中。

 1 package cn.itcast.day5.thread;
 2 
 3 public class 死锁
 4 {
 5     @SuppressWarnings("static-access")
 6     public static void main(String[] args) throws InterruptedException
 7     {
 8         Ticket2 ticket2 = new Ticket2();
 9         Thread thread1 = new Thread(ticket2);
10         Thread thread2 = new Thread(ticket2);
11         thread1.start();
12 
13         thread1.sleep(10);
14 
15         ticket2.flag = false;
16         thread2.start();
17     }
18 }
19 
20 class Ticket2 implements Runnable
21 {
22     int count = 1000;
23     boolean flag = true;
24     Object object = new Object();
25 
26     @Override
27     public void run()
28     {
29         if (flag)
30         {
31             while (true)
32             {
33                 synchronized (object)
34                 {
35                     show();
36                 }
37 
38             }
39         } else
40         {
41             while (true)
42             {
43                 show();
44             }
45         }
46     }
47 
48     public synchronized void show()// this
49     {
50         synchronized (object)
51         {
52             if (count > 0)
53             {
54                 try
55                 {
56                     Thread.sleep(10);
57                     System.out.println(Thread.currentThread().getName() + "....:" + count--);
58                 }
59                 catch (InterruptedException e)
60                 {
61                     e.printStackTrace();
62                 }
63             }
64         }
65 
66     }
67 }
死锁的出现

 

 

 

posted on 2013-05-13 20:39  peter.peng  阅读(526)  评论(0编辑  收藏  举报