Java语言基础-多线程

多线程

    进程:正在进行中的程序(直译);
                       
    线程:就是进程中的一个负责程序执行的控制单元(执行路径);一个进程中可以有多个执行路径,称为多线程。
                    一个进程中,至少要有一个线程。
    多线程存在的意义:开启多个线程,是为了同时运行多部分代码;
                    每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。
    多线程优点:解决了多部分同时运行的问题。
    多线程弊端:线程太多会导致效率的降低。
    其实应用程序的执行都是cpu在做着快速的切换完成的,这个切换是随机的。

    JVM启动时就启动了多个线程,至少有两个线程可以分析得出来:
    1.执行main函数的线程;
            该线程的任务代码都定义在main函数中;
    2.负责垃圾回收的线程。

    创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码就,实现同时运行
    而运行的指定代码就是这个执行路径的任务;
    JVM创建的主线程的任务都定义在了主函数中;而自定义的线程:Thread类用于描述线程,
    线程是需要任务的,所以Thread类也有对任务的描述,这个任务就通过Thread类中的run方法来实现,也就是说,
    run方法就是封装自定义线程运行任务的函数。run方法中定义的就是线程要运行的代码。
    开启线程是为了运行指定代码,所以只有继承Thread类,并覆写run方法,将运行的代码定义在run方法中。

    如何创建线程?
    1.继承Thread类
        步骤:1.定义一个类继承Thread类;
                    2.覆盖Thread类中的run方法;
                    3.直接创建Thread的子类对象创建线程;
                    4.调用start方法,开启线程并调用线程的任务run方法执行。

    可以通过Thread的getName获取线程的名称 Thread-(从0开始)。
    继承Thread类示例:
   

class Demo extends Thread

    {

        private String name;

        Demo(String name)

        {

            super(name);

            //this.name=name;

        }

        public void run()

        {

            for (int x=0;x<10 ;x++ )

            {

                System.out.println(name+"...........x="+x+" name="+Thread.currentThread().getName());

            }

        }

    }



    class ThreadDemo3

    {

        public static void main(String[] args) 

        {

            Demo d1=new Demo("旺财");

            Demo d2=new Demo("xiaoqiang");

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

            d2.start();

            System.out.println("over="+Thread.currentThread().getName());

        }

    }



1.定义类,实现Runnable接口;
2.覆盖接口中的run方法,将线程的任务代码封装到run方法中;
3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递;
    原因:因为线程的任务都封装在Rnnable接口子类对象的Run方法中,所以要在线程对象创建时就必须明确要运行的任务。
4.调用线程对象的start方法,开启线程。
Runnable:它的出现,仅仅是将线程的任务进行了对象的封装。

实现Runnable接口的优点:
1.将线程的任务从线程的子类中分离出来,进行了单独的封装;
    按照面向对象的思想将任务封装成对象;
2.避免了Java单继承的局限性;
所以,创建线程的第二种方法较为常用。

实现Runnable接口示例:

class Demo implements Runnable    //准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行

                                                        //通过接口的形式完成

{

    public void run()

    {

        show();

    }

    public void show()

    {

        for (int x=0;x<10 ;x++ )

        {

            System.out.println(Thread.currentThread().getName()+"......"+x);

        }

    }

}



class ThreadDemo

{

    public static void main(String[] args) 

    {

        Demo d=new Demo();

        Thread t1=new Thread(d);

        Thread t2=new Thread(d);

        t1.start();

        t2.start();

    }

}



线程安全问题产生的原因:
1.多个线程在操作共享的数据;
2.操作共享数据的线程代码有多条;
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,
就会导致线程安全问题的产生。

解决思路:
将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的,
必须要当前线程将这些代码执行完毕后,其他线程才可以参与运算。
在Java中,用同步代码块可以解决这个问题,格式为:
synchronized(对象)
{
    需要被同步的代码;
}

同步的好处:解决了线程的安全问题;
同步的弊端:相对降低了效率,因为同步外的线程都会判断同步锁。
同步的前提:同步中必须有多个线程,并使用同一个锁。
线程安全问题示例、同步代码块:

class Ticket implements Runnable

{

    private int num=20;

    Object obj=new Object();

    public void run()

    {

        sale();

    }

    public void sale()

    {

        while (true)

        {

            synchronized(obj)//如果在此处使用 new Objcet(),则使用的不是同一个锁

            {

                if (num>0)

                {

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

                }

            }

        }

    }

}



class  TicketDemo

{

    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();

    }

}

同步函数使用的锁是this
同步函数示例:

class Ticket implements Runnable

{

    private int num=100;

    //Object obj=new Object();

    boolean flag=true;

    public void run()

    {

        sale();

    }

    public  void sale()

    {

        if(flag)

            while (true)

            {

                synchronized(this)

                {

                    if (num>0)

                    {

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

                    }

                }

            }

        else

            while(true)

                show();

    }

    public synchronized void show()

    {

        if (num>0)

                {

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

                }

    }

}



class  SynFunctionLockDemo

{

    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();

        try

        {

            Thread.sleep(10);

        }

        catch (InterruptedException e)

        {

        }

        t.flag=false;

        t2.start();

    }

}



同步函数和同步代码块的区别:
同步函数的锁是固定的this;
同步代码块的锁是任意的对象。
建议使用同步代码块。
静态的同步函数使用的锁是,该函数所属的字节码文件对象,可以用getClass() 方法获取,也可以用 当前 类名.class()获取
Class cla=t.getClass();
Class cla=Ticket.class;
静态同步函数锁示例:

class Ticket implements Runnable

{

    private static int num=100;

    boolean flag=true;

    public void run()

    {

        sale();

    }

    public  void sale()

    {

        if(flag)

            while (true)

            {

                synchronized(Ticket.class)

                {

                    if (num>0)

                    {

                        try

                        {

                            Thread.sleep(10);

                        }

                        catch (InterruptedException e)

                        {

                        }

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

                    }

                }

            }

        else

            while(true)

                show();

    }

    public static synchronized void show()

    {

        if (num>0)

                {

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

                }

    }

}



class  SynFunctionLockDemo

{

    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();

        try

        {

            Thread.sleep(10);

        }

        catch (InterruptedException e)

        {

        }

        t.flag=false;

        t2.start();

    }

}



多线程下的单例示例:

//饿汉式(单例设计模式)

class Single//类一加载,对象就已经存在

{

    private static Single s=new Single();//在本类中创建对象

    private Single(){};//私有化构造函数

    public static Single getInstance()//公有方法,返回对象

    {

        //多线程中不存在安全隐患

        return s;

    }

}



//懒汉式(延迟加载单例设计模式)

class Single2//类加载进来,没有对象;只有调用了getInstance时,才会创建对象

                    //延迟加载形式

{

    private static Single2 s=null;

    private Single2(){};

    public static Single2 getInstance()

    {

        if (s==null)//加入此行代码,当s不为空时,则不再判断锁,提高效率

        {

            synchronized(Single.class)//静态函数,不能使用getClass()方法

            {

                if(s==null)

                    // -->0   -->1  线程0,1可能存在安全隐患,需要同步锁

					/*

					 *线程0经过判断语句进入,然后执行权切换;

					 *线程1经过判断语句进入,执行权切换;

					 *执行权切换回0,new一个对象,并返回;

					 *执行权切换到2,由于已经经过判断,所以此处不需要再次判断,new一个对象,返回;

					 *造成的后果是有了2个对象,不能保证单例。

					 */

                    s=new Single2();

            }

        }

        return s;

    }

}



class SingleDemo 

{

    public static void main(String[] args) 

    {

        Single s1= Single.getInstance();

        Single s2= Single.getInstance();

        System.out.println(s1==s2);

    }

}



死锁示例:

class Ticket implements Runnable

{

    private static int num=100;

    boolean flag=true;

    public void run()

    {

        sale();

    }

    public  void sale()

    {

        if(flag)

            while (true)

            {

                synchronized(Ticket.class)

                {

                    if (num>0)

                    {

                        try

                        {

                            Thread.sleep(10);

                        }

                        catch (InterruptedException e)

                        {

                        }

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

                    }

                }

            }

        else

            while(true)

                show();//此处线程0获得锁this

    }

    public static synchronized void show()

    {

        if (num>0)

                {

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

                }

    }

}



class  SynFunctionLockDemo

{

    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();

        try

        {

            Thread.sleep(10);

        }

        catch (InterruptedException e)

        {

        }

        t.flag=false;

        t2.start();

    }

}



死锁示例:

class Test implements Runnable

{

    private boolean flag;

    Test(boolean flag)

    {

        this.flag=flag;

    }

    public void run()

    {

        if(flag)

        {

            synchronized(MyLock.locka)

            {//1、线程0获得锁MyLock.locka,继续下一句;尝试获得MyLock.lockb,此时线程1占有MyLock.lockb,无法获得

                System.out.println(Thread.currentThread().getName()+".....if......locka");

                synchronized(MyLock.lockb)

                {

                    System.out.println(Thread.currentThread().getName()+".....if.....lockb");

                }

            }

        }

        else

        {

            synchronized(MyLock.lockb)

            {//2、线程1获得锁MyLock.lockb,继续下一句;尝试获得MyLock.locka,此时线程0占有MyLock.locka,无法获得

                System.out.println(Thread.currentThread().getName()+".....else.....lockb");

                synchronized(MyLock.locka)

                {

                    System.out.println(Thread.currentThread().getName()+".....else.....locka");

                }

            }

        }

    }

}



class MyLock

{

    public static final Object locka=new Object();

    public static final Object lockb=new Object();



}



class DeadLockTest 

{

    public static void main(String[] args) 

    {

        Test a=new Test(true);

        Test b=new Test(false);

        Thread t1=new Thread(a);

        Thread t2=new Thread(b);

        t1.start();

        t2.start();

    }



}



1、线程0获得锁MyLock.locka,继续下一句;尝试获得MyLock.lockb,此时线程1占有MyLock.lockb,无法获得;
2、线程1获得锁MyLock.lockb,继续下一句;尝试获得MyLock.locka,此时线程0占有MyLock.locka,无法获得;
此时,就可能发生死锁。

线程间通信

/*

线程间通信:

多个线程在处理同一资源,但是任务却不同。

*/

//资源

class Resource

{

    String name;

    String sex;

}



//输入

class Input implements Runnable

{

    Resource r;

    Object obj=new Object();

    Input(Resource r)

    {

        this.r=r;

    }

    public void run()

    {

        int x=0;

        while (true)

        {

            synchronized(r)

            {

                if(x==0)

                {

                    r.name="mike";

                    r.sex="male";

                }

                else

                {

                    r.name="lili";

                    r.sex="female";

                }

            }

            x=(x+1)%2;

        }

    }

}



//输出

class Output implements Runnable

{

    Resource r;

    Output(Resource r)

    {

        this.r=r;

    }

    public void run()

    {    

        while(true)

        {

            synchronized(r)

            {

                System.out.println(r.name+"......."+r.sex);

            }

        }

    }

}



class  ResourceDemo

{

    public static void main(String[] args) 

    {

        Resource r=new Resource();

        Input in=new Input(r);

        Output out=new Output(r);

        Thread t1=new Thread(in);

        Thread t2=new Thread(out);

        t1.start();

        t2.start();

    }

}



等待/唤醒机制
涉及的方法:
1.wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
2.notify():唤醒线程池中的一个线程(任意)。
3.notifyAll():唤醒线程池中的所有线程。

这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法,
必须要明确到底操作的是哪个锁上的线程。

为什么操作线程的方法 wait、notify、notifyAll定义在了Object类中?
因为这些方法是监视器的方法,监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方法一定定义在Object类中。

等待唤醒机制示例:

//资源

class Resource

{

    private String name;

    private String sex;

    private boolean flag=false;

    public synchronized void set(String name,String sex)

    {

        if (flag)

        {

            try

            {

                wait();

            }

            catch (InterruptedException e)

            {

            }

        }

        this.name=name;

        this.sex=sex;

        flag=true;

        notify();

    }

    public synchronized void get()

    {

        if(!flag)

            try

            {

                wait();

            }

            catch (InterruptedException e)

            {

            }

        System.out.println(name+"..............."+sex);

        flag=false;

        notify();

    }

}



//输入

class Input implements Runnable

{

    Resource r;

    Object obj=new Object();

    Input(Resource r)

    {

        this.r=r;

    }

    public void run()

    {

        int x=0;

        while (true)

        {                    

                if(x==0)

                {

                    r.set("mike","male");

                }

                else

                {

                    r.set("lili","female");

                }

            x=(x+1)%2;

        }

    }

}



//输出

class Output implements Runnable

{

    Resource r;

    Output(Resource r)

    {

        this.r=r;

    }

    public void run()

    {    

        while(true)

        {

            r.get();

        }

    }

}



class  ResourceDemo3

{

    public static void main(String[] args) 

    {

        Resource r=new Resource();

        Input in=new Input(r);

        Output out=new Output(r);

        Thread t1=new Thread(in);

        Thread t2=new Thread(out);

        t1.start();

        t2.start();

    }

}



if判断标记只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行的问题。
notify只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+nitify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。
多生产者-多消费者示例:

class Resource

{

    private String name;

    private int count=1;

    private boolean flag=false;

    public synchronized void set(String name)

    {

        while(flag)

            try{wait();}catch(InterruptedException e){}

        this.name=name+count;

        count++;

        System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);

        flag=true;

        notifyAll();

    }

    public synchronized void get()

    {

        while(!flag)

        try{wait();}catch(InterruptedException e){}

        System.out.println(Thread.currentThread().getName()+"...消费者......"+this.name);

        flag=false;

        notifyAll();

    }

}



class Producer implements Runnable

{

    private Resource r;

    Producer(Resource r)

    {

        this.r=r;

    }

    public void run()

    {

        while (true)

        {

            r.set("烤鸭");

        }

    }

}

class Consumer implements Runnable

{

    private Resource r;

    Consumer(Resource r)

    {

        this.r=r;

    }

    public void run()

    {

        while (true)

        {

            r.get();

        }

    }

}



class  ProducerConsumerDemo

{

    public static void main(String[] args) 

    {

        Resource r=new Resource();

        Producer pro=new Producer(r);

        Consumer con=new Consumer(r);

        Thread t0=new Thread(pro);

        Thread t1=new Thread(con);

        Thread t2=new Thread(pro);

        Thread t3=new Thread(con);

        t0.start();

        t1.start();

        t2.start();

        t3.start();

    }

}



接口 Lock
同步代码块,对于锁的操作时隐式的
jdk1.5以后,将同步和锁封装成了对象,并将操作锁的方式定义到了该对象中,将隐式动作变成了显式动作。
Lock lock=new ReentrantLock();
lock.lock();//获取锁
code...;//throw Exception();
lock.unlock();//释放锁
try
{
    lock.lock();//获取锁
}
finally
{
    lock.unlock;//释放锁
}

Lock接口:它的出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成了显式锁操作;
同时更为灵活,可以一个锁上加上多组监视器。
    lock();获取锁
    unlock();释放锁,通常需要定义到finally代码块中。

Condition接口:它的出现替代了Object中的wait、notify、notifyAll方法。
                将这些监视器方法单独进行了封装,变成了Condition监视器对象,
                可以和任意锁组合。
    await();//等待
    signal();//唤醒一个等待线程
    signalAll();//唤醒所有等待线程

接口Lock应用示例:

import java.util.concurrent.locks.*;

class Resource

{

    private String name;

    private int count=1;

    private boolean flag=false;

    //创建一个锁对象

    Lock lock=new ReentrantLock();

    //通过已有的锁,获取该锁上的监视器对象

    //Condition con=lock.newCondition();

    //通过已有的锁,获取两组监视器;一组监视生产者,一组监视消费者

    Condition producer_con=lock.newCondition();

    Condition consumer_con=lock.newCondition();

    public void set(String name)

    {

        lock.lock();

        try

        {

            while(flag)

            try{producer_con.await();}catch(InterruptedException e){}

            this.name=name+count;

            count++;

            System.out.println(Thread.currentThread().getName()+"...生产者5.0..."+this.name);

            flag=true;

            consumer_con.signal();

        }

        finally

        {

            lock.unlock();

        }



    }

    public void get()

    {

        lock.lock();

        try

        {

            while(!flag)

            try{consumer_con.await();}catch(InterruptedException e){}

            System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);

            flag=false;

            producer_con.signal();

        }

        finally

        {

            lock.unlock();

        }

        

    }

}



class Producer implements Runnable

{

    private Resource r;

    Producer(Resource r)

    {

        this.r=r;

    }

    public void run()

    {

        while (true)

        {

            r.set("烤鸭");

        }

    }

}

class Consumer implements Runnable

{

    private Resource r;

    Consumer(Resource r)

    {

        this.r=r;

    }

    public void run()

    {

        while (true)

        {

            r.get();

        }

    }

}



class  ProducerConsumerDemo

{

    public static void main(String[] args) 

    {

        Resource r=new Resource();

        Producer pro=new Producer(r);

        Consumer con=new Consumer(r);

        Thread t0=new Thread(pro);

        Thread t1=new Thread(con);

        Thread t2=new Thread(pro);

        Thread t3=new Thread(con);

        t0.start();

        t1.start();

        t2.start();

        t3.start();

    }

}



wait和sleep的区别
1.wait可以指定时间,也可以不指定;
     sleep必须指定时间。
2.在同步中时,对于cpu的执行权和锁的处理不同。
     wait释放执行权,释放锁。
     sleep释放执行权,不释放锁。

停止线程
1.stop方法(已过时);
2.run方法结束;
    怎么控制线程的任务结束?
    任务中都会有循环结构,只要控制住循环就可以结束任务。
    控制循环:通常用定义标记来完成。

如果线程处于冻结状态,无法读取标记,如何结束?
可以使用interrupt()方法,将线程从冻结状态强制恢复到运行状态中来,让线程具备
cpu的执行资格。
但是强制动作会发生InterruptedException,需要处理。

class StopThread implements Runnable

{

    private boolean flag=true;

    public synchronized void run()

    {

        while (flag)

        {

            try

            {

                wait();

            }

            catch (InterruptedException e)

            {

                System.out.println(Thread.currentThread().getName()+",,,,,,,"+e);

                flag=false;

            }

                System.out.println(Thread.currentThread().getName()+"......");

        }

    }

    public void setFlag()

    {

        flag=false;

    }

}



class  StopThreadDemo

{

    public static void main(String[] args) 

    {

        StopThread st=new StopThread();

        Thread t1=new Thread(st);

        Thread t2=new Thread(st);

        t1.start();

        t2.setDaemon(true);//守护线程(后台线程)

        t2.start();

        int num=1;

        for (;;)

        {

            if(++num==50)

            {

                //st.setFlag();

                t1.interrupt();

                //t2.interrupt();

                break;

            }

            System.out.println("main"+num);

        }

        System.out.println("over");

    }

}

你可能感兴趣的:(java语言)