13 多线程

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

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

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

一个进程中至少要有一个线程.

开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的内容,

这个内容可以称为线程要执行的任务.

多线程的好处:解决了多部分同时运行的问题.

多线程的弊端:线程太多回到效率的降低.

其实应用程序的执行都是cpu在做着快速的切换完成的,这个切换是随机的.

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

1.执行main函数的线程

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

2.负责垃圾回收的线程.

  

如何创建一个线程呢?

步骤:

1.定义一个类继承Thread类.

2.覆盖Thread类中的run方法.

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

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

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

而自定义的线程他的任务在哪儿呢?

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

这个任务就通过Thread类中的run方法来体现.也就是说,

run方法就是封装自定义线程运行任务的函数.

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

开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法.

将运行的代码定义在run方法中即可.

3.直接创建Thread的子类对象创建线程.

4.调用start方法开启线程并调用线程的任务run方法执行.

class Test extends Thread
{
    private String name;
    Test(String name)
    {
        this.name =name;
    }

    public void run()
    {
        for(int x=0;x<10;x++)
        {
            System.out.println(name+"x="+x);
        }
    }
}

class Demo
{
    public static void main(String[] args)
    {
        Test d1 =new Test("旺财");
        Test d2 = new Test("xiaoqiang");

        d1.start();//开启线程,调用run方法.
        d2.start();
        System.out.println("over");
    }
}

上述代码有三个线程:主线程,d1和d2开启的两个线程.

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

currentThread返回当前正在进行的线程.

Thread.currentThread().getName();

 

即使主线程先运行结束弹栈了,虚拟机也不会结束.要等所有线程都结束.

 

线程的四种状态:

cpu的执行资格:可以被cpu处理,在处理队列中排队.

cpu的执行权:正在被cpu处理

运行状态:具备着执行资格,具备着执行权

冻结状态:释放执行权的同时,释放执行资格.

临时阻塞状态:具备着执行资格,不具备执行权,正在等待着执行权.

 

创建线程的第一种方式:继承Thread类.

缺点:一旦继承了Thread就不能再继承别的类,因为java中不支持多继承.

可以扩展功能,让其中的内容可以作为线程的任务执行.

通过接口的形式完成.

创建线程的第二种方式:实现Runnable接口.

1.定义类实现runnable接口

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

3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类

的构造函数的参数进行传递.为什么?

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

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

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

class Test implements Runnable//extends Thread
{
    private String name;
    Test(String name)
    {
        this.name =name;
    }

    public void run()
    {
        for(int x=0;x<10;x++)
        {
            System.out.println(name+"x="+x+".."+Thread.currentThread().getName());
        }
    }
}

class Demo
{
    public static void main(String[] args)
    {
        Test d1 =new Test("旺财");
        Test d2 = new Test("xiaoqiang");
        
        Thread t1 = new Thread(d1);
        Thread t2 = new Thread(d2);
        t1.start();
        t2.start();

        //d1.start();//开启线程,调用run方法.
        //d2.start();
        
    }
}

底层大致的实现方式:

class Thread
{
    private Runnable r;
    Thread()
    {

    }
    Thread(Runnable r)
    {
        this.r=r;
    }
    public void run()
    {
        if(r!=null)
            r.run();
    }
    public void start()
    {
        run();
    }
}
class ThreadTmpl implements Runnable
{
    public void run()
    {
        System.out.println("runnable run");
    }
}
ThreadImpl i = new ThreadTmpl();
Thread t = new Thread(i);
t.start();


class SubThread extends Thread
{
    public void run()
    {
        System.out.println("hhah");
    }
}
SubThread s =new SubThread();
s.start();

实现Runnable接口的好处:

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

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

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

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

 

/*
    需求:买票.

*/

class Ticket implements Runnable//extends Thread
{
    private int num=100;
    
    public void run()
    {
        while(true)
        {
            if(num>0)
                System.out.println(Thread.currentThread().getName()+"..sale."+num--);
        }
    }
}

class Demo
{
    public static void main(String[] args)
    {
        Ticket t = new Ticket();//创建一个线程任务对象
        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();

        /*
        Ticket t1 = new Ticket();
        Ticket t2 = new Ticket();
        Ticket t3 = new Ticket();
        Ticket t4 = new Ticket();

        
*/
    }
}

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

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

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

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的

产生.

解决思路:就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,

其他线程不可以参与运算的.

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

在java中,用同步代码块就可以解决这个问题.

同步代码块的格式是:

synchronized(对象)

{

  需要被同步的代码;

}  

class Ticket implements Runnable//extends Thread
{
    private int num=100;
    Object obj = new Object();

    public void run()
    {
        while(true)
        {
            synchronized(obj)
            {
                if(num>0)
                {    
                    try{Thread.sleep(10);}
                    catch(InterruptedException e)
                    {

                    }
                    System.out.println(Thread.currentThread().getName()+"..sale."+num--);
                }
            }
        }
    }
}

class Demo
{
    public static void main(String[] args)
    {
        Ticket t = new Ticket();//创建一个线程任务对象
        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();

        /*
        Ticket t1 = new Ticket();
        Ticket t2 = new Ticket();
        Ticket t3 = new Ticket();
        Ticket t4 = new Ticket();

        
*/
    }
}

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

同步的弊端:相对降低了效率,因为同步外的线程都会判断同步锁.

同步的前提:必须有多个线程并使用同一个锁.

/*
    需求:储户两个,每个都到银行存钱,每次存100,共存三次.

*/

class Bank
{
    private int sum;
    private Object obj = new Object();
    public void add(int num)
    {
        synchronized(obj)
        {
            sum = sum+num;
            //线程可能在此处中断.
            try{Thread.sleep(10);}catch(InterruptedException e){};
            System.out.println("sum="+sum);
        }
    }
}

class Cus implements Runnable
{
    Bank b = new Bank();
    public void run()
    {
        
        for(int x=0;x<3;x++)
        {
            b.add(100);
        }
    }
}


class Demo
{
    public static void main(String[] args)
    {
        Cus c = new Cus();
        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();

    }
}
class Bank
{
    private int sum;
    //private Object obj = new Object();
    public synchronized void add(int num)//同步函数
    {
            sum = sum+num;
            //线程可能在此处中断.
            try{Thread.sleep(10);}catch(InterruptedException e){};
            System.out.println("sum="+sum);
        
    }
}

同步函数使用的锁是this;

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

同步函数的锁是固定的this,就是当前的对象,同步代码块的锁是任意的对象.

建议使用同步代码块.

class Ticket implements Runnable//extends Thread
{
    private int num=100;
   // Object obj = new Object();
    boolean flag = true;

    public void run()
    {
        if(flag)
            while(true)
            {
                synchronized(this)
                {
                    if(num>0)
                    {    
                        try{Thread.sleep(10);}
                        catch(InterruptedException e)
                        {

                        }
                        System.out.println(Thread.currentThread().getName()+"..obj."+num--);
                    }
                }
            }
        else
            while(true)
                this.show();
    }

    public synchronized void show()
    {
        if(num>0)
        {    
            try{Thread.sleep(10);}
            catch(InterruptedException e)
            {

            }
            System.out.println(Thread.currentThread().getName()+"..function."+num--);
        }
    }
}

class Demo
{
    public static void main(String[] args)
    {
        Ticket t = new Ticket();//创建一个线程任务对象
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);

            t1.start();
            try{
                Thread.sleep(10);
            }
            catch(InterruptedException e){}

            t.flag = false;
            t2.start();
       

    }
}
class Ticket implements Runnable//extends Thread
{
    private static int num=100;
   // Object obj = new Object();
    boolean flag = true;

    public void run()
    {
        if(flag)
            while(true)
            {
                synchronized(this.getClass())//Ticket.class
                {
                    if(num>0)
                    {    
                        try{Thread.sleep(10);}
                        catch(InterruptedException e)
                        {

                        }
                        System.out.println(Thread.currentThread().getName()+"..obj."+num--);
                    }
                }
            }
        else
            while(true)
                this.show();
    }

    public static synchronized void show()
    {
        if(num>0)
        {    
            try{Thread.sleep(10);}
            catch(InterruptedException e)
            {

            }
            System.out.println(Thread.currentThread().getName()+"..function."+num--);
        }
    }
}

class Demo
{
    public static void main(String[] args)
    {
        Ticket t = new Ticket();//创建一个线程任务对象
        
        //class clazz = t.getClass();
        
        
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
    
            t1.start();
            try{
                Thread.sleep(10);
            }
            catch(InterruptedException e){}

            t.flag = false;
            t2.start();
       
    }
}

静态的同步函数使用的锁是:该函数所属字节码文件对象,可以用getClass获取

,也可以用当前类名.class表示.

 

多线程下的单例:

1.饿汉式(没问题)

2.懒汉式(同步有问题)

//饿汉式
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()//synchronized可以同步函数
    {
        if(s==null)//提高效率
        {
            synchronized(Single.class)//getClass()方法是非静态的.不能用在这里
            {
             if(s==null)
                //-->0  -->1 
                s=new Single();
            }
        }
        return s;
        
    }
}

class Demo
{
    public static void main(String[] args)
    {
        
       
    }
}

 

posted on 2016-12-11 23:08  夜行锦衣  阅读(122)  评论(0编辑  收藏  举报

导航