Java基础——多线程

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

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

2、创建线程

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

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

第一种方式代码:

可以通过Thread的getName()获取线程的名称Thread-编号(从0开始)
主线程的名字就是:main 可以通过Thread.currentThread().getName()获取正在运行的线程

class Demo  extends Thread  //第一步
{
    private String name;
    Demo(String name)
    {
        this.name=name;
    }
    public void run()       //第二步,覆盖run()方法,Thread类中有这个方法
    {
        for(int x=0;x<10;x++)
        {
            System.out.println(name+"....x="+x+"...."+getName());//getName()是Thread的方法,用来获取线程的名字
        }
    }
}
class ThreadDemo2 
{
    public static void main(String[] args) 
    {
        /*
        创建线程的目的是为了开启一条执行路径,去运行制定的代码和其他代码同时执行
        而运行的指定代码就是这个执行路径的任务。
        jvm创建的主线程的任务都定义在了主函数中
        而自定义的主线程的任务都定义在哪儿呢?
        Tread类用于描述线程,线程是需要任务的,所以Thread类就是对任务的描述
        这个任务就是通过Thread类中的run方法来体现的,也就是说,run 方法就是封装自定义
        线程运行任务的函数
        run方法中定义的就是线程要运行的任务代码
        开启线程是为了运行指定代码,所以只有继承Thread类并覆写run方法并
        将运行的代码定义在run方法中即可
        */
        Demo d1=new Demo("旺财"); //第三步  创建子类对象
        Demo d2=new Demo("xiaoqiang");  //创建线程
        d1.start();                 //父类Thread中的方法,开启线程并调用run方法
        System.out.println("haha...."+Thread.currentThread().getName());
        d2.start();

    }
}

创建线程的第二种方式代码:

class Demo implements Runnable      //准备拓展Demo类的功能,让其中的内容可以作为线程的任务执行
{       //第一步定义类实现Runnable接口                    //通过接口的形式完成

    public void run()//第二步 覆盖接口中的Run方法,将线程的任务代码封装到run方法中
    {
        show();
    }
    public void show()
    {
        for (int x=0;x<20;x++)
        {
            System.out.println(Thread.currentThread().getName()+"....."+x);
        }
    }
}
class ThreadDemo3 
{
    public static void main(String[] args) 
    {
        Demo d=new Demo();
        Thread t1=new Thread(d);//第三步 通过线程类创建对象
        Thread t2=new Thread(d);
        t1.start();//第四步 调用线程对象的start方法开启线程
        t2.start();

    }
}

两种方式比较:

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

3、线程安全问题

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

解决思路:
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候其他线程不可以参与运算。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算

在java中,用同步代码块就可以解决这个问题
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码;
}
对象可以是任意对象

同步的好处:解决了线程的安全问题
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁

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

安全问题示例代码:

class Ticket implements Runnable
{
    private int num=100;
    Object obj=new Object();
    public void run()
    {
        //Object obj=new Object();//放在这个位置的时候就不是使用的同一个锁了,相当于每个线程都有一个锁,每个锁只有一个线程
        while(true)
        {
            /*
            下面可能出现线程安全问题,当num=1时,线程1执行完num>0的时候,还没执行下面的语句CPU就切换到线程2(这个时候num还等于1),并执行了
            下面的一条语句使得num=0,此时再切换到线程1,num=-1,导致出错
            原因:多个线程共享了数据num  操作共享数据的线程代码有两条 num>0 以及下面的num--

            if(num>0)
            {
                System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
            }
            */
            synchronized(obj)//相对于一个锁,每次判断下,如果有线程在执行里面的数据,其他线程就进不来
            {
                if(num>0)
                {
                    System.out.println(Thread.currentThread().getName()+"...sale..."+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();
    }
}

同步函数和同步代码块实现方式:

class Bank
{
    private int sum;
    private Object obj=new Object();
    /*
    解决办法二
    */
    public synchronized void add(int num)//同步函数
    {

            sum=sum+num;
            System.out.println("sum="+sum);
    }

    /*
    解决办法一 同步代码块
    public void add(int num)
    {
        synchronized(obj)
        {
            sum=sum+num;
            System.out.println("sum="+sum);
        }
    }
    */
}
class Cus implements Runnable
{
    private Bank b=new Bank();
    public void run()
    {

        for(int x=0;x<3;x++)
        {
            b.add(100);//add方法也是线程运行的代码,add方法中有两条语句,所以存在线程安全隐患
        }
    }
}
class BankDemo 
{
    public static void main(String[] args) 
    {
        Cus c=new Cus();
        Thread t1=new Thread(c);
        Thread t2=new Thread(c);
        t1.start();
        t2.start();
    }
}

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

同步函数和同步代码块的区别:
同步函数的锁是固定的this对象,同步代码块的锁是任意的对象,建议使用同步代码块。

静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass获取
也可以用当前类名.class表示
this.getClass()是非静态的

示例代码:

class Ticket implements Runnable
{
    private static int num=100;
    boolean flag=true;
    public void run()
    {
        while(true)
        {
            synchronized(Ticket.class)//this.getClass()
            {
                if(num>0)
                {
                    System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
                }
            }
        }
    }
    public static synchronized void show()//静态同步函数
    {
        if(num>0)
        {
            System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
        }
    }
}


class SynFunctionChronized 
{
    public static void main(String[] args) 
    {
        Ticket t=new Ticket();

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


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

单例模式的线程问题


/*
线程下的单例模式,建议使用饿汉式
*/
//饿汉式
class Singele
{
    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()
    {
        if(s==null)//可以提高效率
        {
            synchronized(Single.class)//解决线程的安全问题
            {
                if(s==null)
                    s=new Single();
            }
        }
        return s;
    }
}
class  SingleDemo
{
    public static void main(String[] args) 
    {
        System.out.println("Hello World!");
    }
}

死锁

死锁案例:

/*
死锁:常见情景之一:同步的嵌套

*/
class Ticket implements Runnable
{
    private static int num=100;
    boolean flag=true;
    Object obj=new Object();
    public void run()
    {
        if(flag)
            while(true)
            {
                synchronized(obj)//该程序死锁了,这个地方拿到了obj锁
                {
                    show();//这是同步函数,拿到的是this锁,同步函数里面又要等待obj锁释放
                }
            }
        else
            while(true)
                this.show();//这个位置拿着this锁,里面等待着obj锁释放
    }
    public synchronized void show()
    {
        synchronized(obj)
            {
                if(num>0)
                {
                    System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
                }
            }
    }
}


class DeadLockDemo 
{
    public static void main(String[] args) 
    {
        Ticket t=new Ticket();

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


        t1.start();
        t.flag=false;
        t2.start();
    }
}
/*
死锁案例
*/
class Test implements Runnable
{
    private boolean flag;
    Test(boolean flag)
    {
        this.flag=flag;
    }
    public void run()
    {
        /*
        线程1拿到a锁,等待b锁
        线程2拿到b锁,等待a锁时变成了死锁

        当然也有可能和谐掉了:线程1拿到a锁并拿到了b锁释放a锁,线程2拿到a锁释放b锁
        */
        if(flag)
        {
            synchronized(MyLock.locka)
            {
                System.out.println(Thread.currentThread().getName()+"...if locka...");
                synchronized(MyLock.lockb)
                {
                    System.out.println(Thread.currentThread().getName()+"if lockb...");
                }
            }
        }
        else
        {
            synchronized(MyLock.lockb)
            {
                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();
    }
}

生产者消费者问题

/*
生产者,消费者

多生产者,多消费者问题
if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况
while判断标记解决了线程获取执行权后是否要运行

notify只能唤醒一个线程,如果本方唤醒了本方,没有意义,而且while判断标记
+notify会导致死锁
notifyAll解决了本方线程一定会唤醒对方线程的问题
*/
class Resource
{
    private String name;
    private int count=1;
    private boolean flag=false;
    public synchronized void set(String name)
    {
        while(flag)//这个位置如果像单生产者,消费者一样用if判断标记则会导致数据错误的情况
            try{this.wait();}catch(InterruptedException e){}
        this.name=name+count;
        count++;
        System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
        flag=true;
        notifyAll();//这个位置如果像但生产者,消费者一样用notify则会导致死锁情况的产生
    }
    public synchronized void out()
    {
        while(!flag)
            try{this.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 Custumer implements Runnable
{
    private Resource r;
    Custumer(Resource r)
    {
        this.r=r;
    }
    public void run()
    {
        while(true)
        {
            r.out();
        }

    }
}
class   ProducerCustumerDemo
{
    public static void main(String[] args) 
    {
        Resource r=new Resource();

        Producer pro=new Producer(r);
        Custumer cus=new Custumer(r);

        Thread t1=new Thread(pro);
        Thread t2=new Thread(cus);
        t1.start();
        t2.start();
    }
}
/*
jdk1.5以后将同步和锁封装成了对象
并将操作锁的隐式方式定义到了该对象中
将隐式动作变成了显示动作

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

Condition接口:出现替代了Object中的wait notify notifyAll方法,
                将这些监视器方法单独进行了封装,变成Condition监视器对象
                可以任意锁进行组合
await()
signal()
signalAll()
*/
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 custumer_con=lock.newCondition();
    public  void set(String name)
    {
        lock.lock();
        try
        {
            while(flag)
            //try{this.wait();}catch(InterruptedException e){}
            try{producer_con.await();}catch(InterruptedException e){}
            this.name=name+count;
            count++;
            System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
            flag=true;
            //notifyAll();
            custumer_con.signal();
        }
        finally
        {
            lock.unlock();
        }

    }
    public void out()
    {
        lock.lock();
        try
        {
            while(!flag)
            //try{this.wait();}catch(InterruptedException e){}
            try{custumer_con.await();}catch(InterruptedException e){}
            System.out.println(Thread.currentThread().getName()+"...消费者......"+this.name);
            flag=false;
            //notifyAll();
            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 Custumer implements Runnable
{
    private Resource r;
    Custumer(Resource r)
    {
        this.r=r;
    }
    public void run()
    {
        while(true)
        {
            r.out();
        }

    }
}
class   ProducerCustumerDemo2
{
    public static void main(String[] args) 
    {
        Resource r=new Resource();

        Producer pro=new Producer(r);
        Custumer cus=new Custumer(r);

        Thread t1=new Thread(pro);
        Thread t2=new Thread(cus);
        t1.start();
        t2.start();
    }
}

wait和sleep的区别

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

停止线程

/*
停止线程:
1、stop方法(不推荐使用)

怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。

控制循环通常用
*/
class StopThread implements Runnable
{
    private boolean flag=true;
    public void run()
    {
        while(flag)
        {
            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.start();

        int num=1;
        for(;;)
        {
            if(++num==50)
            {
                st.setFlag();
                break;
            }
            System.out.println("main...."+num);
        }
        System.out.println("over");
    }
}
/*
任务都会有循环结构,只要控制住循环就可以结束任务

控制循环通常就用定义标记来完成

但是如果线程处于了冻结状态,无法读取标记

可以使用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(e+"..."+e);

            }
            System.out.println(Thread.currentThread().getName()+"......+++");
        }
    }
    public void setFlag()
    {
        flag=false;
    }
}
class  StopThreadDemo1
{
    public static void main(String[] args) 
    {
        StopThread st=new StopThread();

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

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

        int num=1;
        for(;;)
        {
            if(++num==50)
            {
                //st.setFlag();
                t1.interrupt();
                t2.interrupt();
                break;
            }
            System.out.println("main..."+num);
        }
        System.out.println("Hello World!");
    }
}

多线程误区

class Test implements Runnable  //错在这,因为没有覆盖接口中的抽象方法,所以必须Test必须是抽象类,然而并不是
{
    public void run(Thread t) //run没有参数
    {

    }
}
//如果错误,错误发生在哪一行?
class ThreadTest 
{
    public static void main(String[] args) 
    {
        new Thread(new Runnable()
        {
            public void run()
            {
                System.out.println("runnable run");
            }
        })//括号里面的是任务
        //下面的这个类是Thread的子类
        //调用顺序:有子类的先以子类为主、任务、自身
        //所以结果是:subThread run
        {
            public void run()
            {
                System.out.println("subThread run");
            }
        }.start();
        /*
        new Thread()
        {
            public void run()
            {
                for(int x=0;x<50;x++)
                {
                    System.out.println(Thread.currentThread().getName()+"...x="+x);
                }
            }
        }.start();
        for(int x=0;x<50;x++)
        {
            System.out.println(Thread.currentThread().getName()+"...y="+x);
        }
        Runnable r=new Runnable()
        {
            public void run()
            {
                for(int x=0;x<50;x++)
                {
                    System.out.println(Thread.currentThread().getName()+"...z="+x);
                }
            }
        };
        new Thread(r).start();
        */
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值