15-多线程

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

一、线程名称

// 显示主线程名
Thread t = Thread.currentThread();
System.out.println("当前的线程名字是:" + t.getName());
t.setName("MyJavaThread");
System.out.println("当前的线程名字是:" + t.getName());

此时会打印,主线程名字 main,修改线程名称后打印 MyJavaThread。

二、使用一个新的线程执行代码

编写一个类继承Thread,并且重写其run方法:

public class MyThread extends Thread
{
    @Override
    //重写run方法
    public void run() {
        // TODO Auto-generated method stub
        //super.run();
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }   
}

在main中创建一个线程对象,并且启动执行,分别打印1,2,3,4......100。

// 继承Thread类创建新线程
MyThread t = new MyThread();
t.start(); //启动线程

此时run方法中的循环是脱离程序执行的主线程的,以一个新的线程来执行代码。

三、多个线程交替执行

编写一个类继承Thread,并且重写其run方法:

public class MyThread extends Thread
{
    @Override
    //重写run方法
    public void run() {
        // TODO Auto-generated method stub
        //super.run();
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }   
}

在main中创建一个多个线程对象,并且启动执行。

MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start(); //启动线程
t2.start(); //启动线程
t3.start(); //启动线程

此时循环执行300次,并且是三个线程交替执行的。

四、继承Runnable接口实现多线程

编写一个类继承Runnable,并且重写其run方法:

public class MyRunnable implements Runnable 
{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }   
    }
}

在main中创建一个多个线程对象,并且启动执行。

MyRunnable myRun = new MyRunnable();
Thread t1 = new Thread(myRun);
Thread t2 = new Thread(myRun);
Thread t3 = new Thread(myRun);
t1.start();
t2.start();
t3.start();

此时循环执行300次,并且是三个线程交替执行的。

由于Runnable是一个接口,所以可以避免单继承的局限性。

五、使用多线程模拟抢票

版本一:

编写一个类继承Thread,并且重写其run方法:

public class MyThread extends Thread
{
    private int count = 20;  //一共20张票
    private int num=0; //票的编号,自动增长,例如(1,2,3,4,。。。。)
    @Override
    //给方法加锁,设置同步方法,此时线程休眠Thread.sleep(500),锁并没有释放,所以其它线程仍然不能访问方法
    public void run() {
        while(true)
        {
            if(count <= 0)   //票全部卖完的情况下,直接退出循环
                break;
            num++;  //票号自动生成
            count--; //票的数量自动减少
            try {
                Thread.sleep(100); //模拟网络延时
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "抢票成功,票号:" + num + ",票剩余:" + count);
        }
    }

}

在main方法中模拟三个人进行抢票:

MyThread t1 = new MyThread();
t1.setName("刘备");
MyThread t2 = new MyThread();
t2.setName("关羽");
MyThread t3 = new MyThread();
t3.setName("张飞");
System.out.println("刘关张开始抢票:");
t1.start();
t2.start();
t3.start();

此时,我们发现一共20张票,但是每个人都可以抢到20张票。不符合逻辑,无法实现MyThread类中的票的数量的资源共享。

版本二:

编写一个类继承Runnable,并且重写其run方法:

public class MyRunnable implements Runnable 
{
    private int count = 20;  //一共20张票
    private int num=0; //票的编号,自动增长,例如(1,2,3,4,。。。。)
    @Override
    public void run() {
        while(true)
        {
            if(count <= 0)   //票全部卖完的情况下,直接退出循环
                break;
            num++;  //票号自动生成
            count--; //票的数量自动减少
            try {
                Thread.sleep(100); //线程休眠,模拟网络延时
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "抢票成功,票号:" + num + ",票剩余:" + count);
        }
    }
}

在main方法中模拟三个人进行抢票:

MyRunnable myRun = new MyRunnable();
Thread t1 = new Thread(myRun);
t1.setName("刘备");
Thread t2 = new Thread(myRun);
t2.setName("关羽");
Thread t3 = new Thread(myRun);
t3.setName("张飞");
System.out.println("刘关张开始抢票:");
t1.start();
t2.start();
t3.start(); 

此时,我们发现一共20张票,也实现了3个人一共抢票20张,但是存在了多个线程同时访问num变量的情况,所以存在多个人抢到了同一张票的情况,即票号相同,也不符合逻辑。

版本三:

编写一个类继承Runnable,并且重写其run方法:

public class MyRunnable implements Runnable 
{
    private int count = 20;  //一共20张票
    private int num=0; //票的编号,自动增长,例如(1,2,3,4,。。。。) 
    @Override
    //使用同步方法synchronized来优化,将整个方法加锁,没有退出方法,其它线程无法调用方法。
    public synchronized  void run() {
        while(true)
        {
            if(count <= 0)   //票全部卖完的情况下,直接退出循环
                break;
            num++;  //票号自动生成
            count--; //票的数量自动减少
            try {
                Thread.sleep(100); //模拟网络延时
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "抢票成功,票号:" + num + ",票剩余:" + count);
        }   
    }
}

在main方法中模拟三个人进行抢票:

MyRunnable myRun = new MyRunnable();
Thread t1 = new Thread(myRun);
t1.setName("刘备");
Thread t2 = new Thread(myRun);
t2.setName("关羽");
Thread t3 = new Thread(myRun);
t3.setName("张飞");
System.out.println("刘关张开始抢票:");
t1.start();
t2.start();
t3.start();

此时,我们为了解决多个人同时抢到同一张票的情况,给run方法进行了synchronized关键字加锁,加锁之后,当前线程的run方法没有退出,其它线程无法访问run方法,解决了多个人抢到同一张票的问题,但是又出现了新问题,即一个人把所有的票都抢光了。

最终版本:

编写一个类继承Runnable,并且重写其run方法:

public class MyRunnable implements Runnable 
{
    private int count = 20;  //一共20张票
    private int num=0; //票的编号,自动增长,例如(1,2,3,4,。。。。)
    @Override
    public void run() {
        while(true)
        {
            synchronized(this) //给代码块加锁
            {
                if(count <= 0)   //票全部卖完的情况下,直接退出循环
                    break;
                num++;  //票号自动生成
                count--; //票的数量自动减少
                System.out.println(Thread.currentThread().getName() + "抢票成功,票号:" + num + ",票剩余:" + count);
            }
            try {
                Thread.sleep(100); //模拟网络延时
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

此时,我们给固定的代码块加锁,解决了上述所有问题。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容