当前位置:主页 > 查看内容

JAVA 多线程

发布时间:2021-05-09 00:00| 位朋友查看

简介:多线程 在看多线程之前先来看看必要的一些东西 线程与进程 进程 是指一个内存中运行的应用程序每个进程都有一个独立的内存空间。 线程 是进程中的一个执行路径共享一个内存空间线程之间可以自由切换并发执行. 一个进程最少有一个线程。 线程实际上是在进程基……

多线程

在看多线程之前先来看看必要的一些东西:

线程与进程

进程:
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。
线程:
是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程。
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。

同步与异步

同步: 排队执行 , 效率低但是安全.
异步: 同时执行 , 效率高但是数据不安全.

并发与并行

并发: 指两个或多个事件在同一个时间段内发生。
并行: 指两个或多个事件在同一时刻发生(同时发生)。

好了这些相关的知识了解一下就可以看看多线程了。

线程的定义

1.继承Thread
继承java.lang.Thread类,重写run()方法 。
格式如下:

//这是继承格式,记住一定要重写run()方法
public class MyThread extends Thread {

    //run方法就是线程要执行的任务方法

    @Override
    public void run() {
        //这里的代码就是一条新的执行路径
        //这个执行路径是触发方式,不是调用run方法,而是通过thread对象的start方法来启动任务
    }
}
//这个是创建格式,和之前类的对象的创建是一样的
//但是一定要记住要用"对象.start()"来启动线程,要不然就是线程创建了但是不会运行
public class Demo1 {
    public static void main(String[] args) {

        MyThread m = new MyThread();
        m.start();
    }
}

上述就完成并启动了一个线程。
2.实现Runnable
实现java.lang.Runnable接口,并将其传入到Thread类对象中
格式如下:

//与上述Thread格式一模一样,就是一个是继承类,一个是实现接口
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        //线程的任务
    }
}
//创建格式
public class Demo1 {
    public static void main(String[] args) {

        //实现runnable
        //1 创建一个任务对象
        MyRunnable r = new MyRunnable();
        //创建一个线程并给他一个任务
        Thread t = new Thread(r);
        //启动线程
        t.start();
    }
}
//这里就是比Tread多了一步,就是给他一个任务。

那么这两种方法到底那种好呢,有人觉得1好应为1比2少一步,不用分配任务,但是实际上是2比较好。
来看看,实现Runnable与继承Thread相比有如下优势:
1、通过创建任务,然后给线程分配任务的方式实现多线程,更适合多个线程同时执行任务的情况
2、可以避免单继承所带来的局限性
3、任务与线程是分离的,提高了程序的健壮性
4、后期学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程。

线程安全问题

先来看看什么是线程安全问题

package thread;


public class Demo7 {
    public static void main(String[] args) {
        //线程不安全
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        @Override
        public void run() {
            while (count>0){
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println("卖票结束,余票:"+count);
            }
        }
    }
}

读者可以试试这个代码,有时候也要多运行几次,它会出现以下情况:
在这里插入图片描述
我们可以看到在运行结果中出现了负数,也就是说票买到了负的,你想想这肯定是不符合逻辑的,但是程序也没有出错,也没有异常,这就是线程不安全,也叫叫做线程安全问题。下面就看看线程不安全的解决方法。
解决方案1 同步代码块
格式如下:

synchronized(锁对象){

}

也就是将上述代码修改为如下:

package thread;


//线程同步synchronized

public class Demo8 {
    public static void main(String[] args) {
        Object o = new Object();

        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        //这个锁是公共的
        private Object o = new Object();
        @Override
        public void run() {
            //Object o = new Object();    //这里不是同一把锁,所以锁不住
                while (true) {
                    synchronized (o) {
                        if (count > 0) {
                         //卖票
                            System.out.println("正在准备卖票");
                            try {
                            Thread.sleep(1000);
                            } catch (InterruptedException e) {
                            e.printStackTrace();
                            }
                            count--;
                            //打印线程名和票的余数
                            System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                        }else {
                            break;
                        }

                }
            }
        }
    }
}

有的读者会问,上面怎么有两个地方有“Object o = new Object(); ”这个语句,在上面的一个叫做公共锁,下面的一个叫做自己的锁,只用公共锁才能锁的住线程,打个比方,你去试衣间试衣服,门上肯定有一把锁,这个可以叫做公共锁,要是是锁住的别人就不会进来,如果试衣服的人要进试衣间,进去之前不是看门上的锁,而是看自己口袋的锁(也就是自己的锁)的话,那么自己的锁肯定一开始是开的,那么来一个人看一下自己的锁(是开的可以进试衣间),就进去了,那不是里面得打起来?
解决方案2 同步方法
将上述代码修改为如下:

package thread;



//线程同步synchronized

public class Demo9 {
    public static void main(String[] args) {
        Object o = new Object();

        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        @Override
        public void run() {

            while (true) {
                boolean flag = sale();
                if(!flag){
                    break;
                }
            }
        }
        public synchronized boolean sale(){
            if (count > 0) {
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                return true;
            }
                return false;

        }
    }
}

也很容易理解,就是写方法来限制。
解决方案3 显示锁 Lock 子类 ReentrantLock
将上述代码修改为如下:

package thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


//同步代码块和同步方法都属于隐式锁
//线程同步lock

public class Demo10 {
    public static void main(String[] args) {
        Object o = new Object();

        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        //参数为true表示公平锁    默认是false 不是公平锁
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            while (true) {
            //锁死
                l.lock();
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                    }else {
                        break;
                    }
                    //开锁
                    l.unlock();
            }
        }
    }
}

就是将程序执行的时候将线程锁死,一个线程执行完后又打开,就可以将其理解为生活当中正在的试衣间,一个人来试衣服,试衣服的时候就将门锁住,试完衣服就开锁,就是这样。

多线程通信问题

现在想想有这样一个问题,一个厨师和一个服务员,初始做好一个菜,服务员就端一个菜,顺序和味道不能弄混淆,来看下面的程序:

package thread;


public class Demo12 {
    public static void main(String[] args) {
        //多线程通信    生产者与消费者问题
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }
    //厨师
    static class Cook extends Thread{
        private Food f;

        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if(i%2==0){
                    f.setNameAndTaste("老干妈小米粥","香辣味");
                }else {
                    f.setNameAndTaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务员
    static class Waiter extends Thread{
        private Food f;

        public Waiter(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;
        public void setNameAndTaste(String name,String taste){
            this.name = name;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.taste = taste;
        }
        public void get(){
            System.out.println("服务员端走的菜的名称是:" + name + "味道是:" + taste);
        }
    }
}

施行结果会出现这样的情况:
在这里插入图片描述
我们看到了这样的语句:
服务员端走的菜的名称是:煎饼果子味道是:香辣味
服务员端走的菜的名称是:煎饼果子味道是:甜辣味
欸,出现了语句错误,因为两个线程在运行时,可能一个线程运行的时候另一个没反应过来,或者说是反应慢了的原因就导致了这种情况。
解决方法,如下代码:

package thread;


public class Demo12 {
    public static void main(String[] args) {
        //多线程通信    生产者与消费者问题
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }
    //厨师
    static class Cook extends Thread{
        private Food f;

        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if(i%2==0){
                    f.setNameAndTaste("老干妈小米粥","香辣味");
                }else {
                    f.setNameAndTaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务员
    static class Waiter extends Thread{
        private Food f;

        public Waiter(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;
        //true表示可以生产
        boolean flag = true;
        public synchronized void setNameAndTaste(String name,String taste){
            if(flag){
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
        public synchronized void get(){
            if(!flag){
                System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

和前面的线程安全一样添加同步方法一样,将get方法改为同步方法,就可以了。

;原文链接:https://blog.csdn.net/m0_51035411/article/details/115421004
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!
上一篇:再战JVM (1) 类加载过程 下一篇:没有了

推荐图文

  • 周排行
  • 月排行
  • 总排行

随机推荐