java多线程

目录

一、线程

1、进程

01.进程特点

02.并发和并行区别

2、线程

3、线程优点

二、线程的创建方式:

1、继承Thread类

2、实现Runnable接口

3、实现Callable接口

4、三种创建线程的比较

三、线程生命周期

四、控制线程手段

1、线程休眠

2、 等待线程

3、后台线程

4、线程优先级

五、有关线程安全

 1.线程不安全示例

2、解决线程安全方法

01、同步代码块

02、同步方法块

03、同步锁

六、死锁

1、死锁示例:

2、解决方法

一、线程

1、进程

* 进程:是处于运行过程中的程序、具有独立的功能、是系统进行资源分配和调度的独立单位

01.进程特点

1、独立性:进程是系统中独立存在的实体,他们拥有自己独立的资源,每个进程都拥有自己私有的抵制空间,在没有经过进程本身允许下、一个进程不能直接访问其他进程的地址空间
2、动态性:进程是一个正在系统中活动的指令集合,包含了时间的概念,具有自己的生命周期状态;
3、并发性:在单个处理器上,多线程可以并发执行,并且在执行时他们彼此之间不会互相的影响。

02.并发和并行区别

并行 : 在同一时刻,有多条指令在多个处理器上同时执行
并发 :
        1、在同一时刻,某个处理器只能执行一条指令。
        2、多个进程的指令可以被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果。

2、线程

1、线程扩展进程的概念,使得同一个进程可以同时并发处理多个任务
2、线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程
3、线程不拥有系统资源,他与父线程中的其他线程共享父进程所拥有的全部系统资源

3、线程优点

1、容易共享内存: 进程之间不能够共享内存,但线程之间可以共享内存,因为与分隔的进程相比,线程之间的隔离度比较小,他们共享内存、文件句柄、其他每个进程应有的状态
2、运行效率更高:系统创建进程时候要为其分配系统资源,但创建线程时不需要,所以线程的运行效率更高
3、编程方式简单:JAVA内置了很多多线程功能的支持,并不是简单的对操作系统的底层进行调度,编程更方便。

二、线程的创建方式:

* java的线程模型
        1、JAVA中使用Thread类代表线程,所有的线程对象都是必须是Thread类或子类的实例
        2、线程用于完成一定的任务(执行一段程序流),JAVA使用线程执行体来表示折断程序流
        3、线程的使用过程:定义线程的执行体-->创建线程对象-->调用线程对象的方法启动线程

1、继承Thread类

        1、定义Thread类的子类,并重写该类的run()方法(线程体)。
        2、创建Thread类的子类的实例,即创建线程对象。
        3、调用线程对象的start()方法,启动这个线程。
    public static void main(String[] args) {


        //子线程
        //启动继承与Thread线程
        new First().start();
        new First().start();//线程支持并发,两个线程并发执行

    }
    /**
     * 继承与Thread方式
     */
    private static class First extends Thread{

        @Override
        public void run() {
            //重写线程体
           for (int i = 0; i < 5; i ++){
               System.out.println(Thread.currentThread().getName() + "\t" + i);//输出线程明名
           }
        }
    }

2、实现Runnable接口

1、定义Runnable接口实现类,并实现该接口的run()方法(线程体)
2、创建Runnable实现类的实例,并以此作为target来创建Thread对象
3、调用Thread对象的start()方法启动线程
    public static void main(String[] args){


        //启动实现Runnable接口线程
        new Thread(new Second()).start();
        new Thread(new Second()).start();
    }
    /**
     * 实现Runnable接口
     */
    private static class Second implements Runnable{

        @Override
        public void run() {
            //重写线程体
            for (int i = 0; i < 5; i ++){
                System.out.println(Thread.currentThread().getName() + "\t" + i);//输出线程明名
            }
        }
    }

3、实现Callable接口

1、定义Callable接口实现类,并实现该接口的call()方法(线程体)
2、创建Callable接口的实例,并使用Future接口来包装Callable对象,最后使用Future对象(线程返回值)作为target来创建Thread对象
3、调用Thread对象的start()方法来启动线程
4、调用Future对象的get()方法获取线程的返回值。
public static void main(String[] args) throws ExecutionException, InterruptedException {


        //启动实现Callable接口的线程
        FutureTask<String> task1 = new FutureTask<>(new Third());
        FutureTask<String> task2 = new FutureTask<>(new Third());
        new Thread(task1).start();
        new Thread(task2).start();

  
        //启动实现Callable接口的线程的线程名---采用阻塞方法
        System.out.println("返回值 - 0 :\t" + task1.get());
        System.out.println("返回值 - 1 :\t" + task2.get());

    }
 /**
     * 实现Callable接口
     */
    private static class Third implements Callable<String>{

        @Override
        public String call() throws Exception {
            for (int i = 0; i < 5; i ++){
                System.out.println(Thread.currentThread().getName() + "\t" + i);//输出线程明名
            }
            return Thread.currentThread().getName();//返回线程名
        }
    }

4、三种创建线程的比较

* 继承父类的方式:丧失了灵活性
        * 优点:编程简单
        * 缺点:线程类已经继承了Thread类,所以不能在继承其他的父类
* 实现接口的方式
        * 优点:线程类只实现了接口,还可以继承与其他的父类
        * 缺点:编程比较麻烦

三、线程生命周期

* 线程的生命周期
    * 当线程被创建之后,不会立刻进入运行状态,也不会一直处于运行状态。(如果一直运行,就无法实现并发)
    * 线程的生命周期要经过5个状态:
        1、新建(new)
        2、就绪(Ready)
        3、运行(Running)
        4、阻塞(Blocked)
        5、死亡(Dead)
/**
 * 线程生命周期
 */
public class demo2 {
    public static void main(String[] args) throws InterruptedException {
        //启动实现Runnable接口线程

        //新建状态
        Thread thread0 = new Thread(new Second());
        Thread thread1 = new Thread(new Second());
        for (int i = 0; i < 20; i ++) {
            System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
            if (i == 5){
                //子线程就绪状态,等待被调度
                thread0.start();
                thread1.start();
//                thread1.start();//就绪之后再就绪,会报错,只能对新建状态的线程调用start()
                //强制主线程就绪
//                Thread.yield();//不确定强制主线程就绪,
                //设置主线程阻塞,主线程阻塞1ms,使得thread0,和thread1执行
                Thread.sleep(1);
            }
        }


        }
    /**
     * 实现Runnable接口
     */
    private static class Second implements Runnable{

        @Override
        public void run() {
            //重写线程体
            for (int i = 0; i < 5; i ++){
                System.out.println(Thread.currentThread().getName() + "\t" + i);//输出线程明名
            }
        }
    }
}

四、控制线程手段

1、线程休眠

Thread类提供了休眠方法,可以让当前线程暂停一段时间
        1、static void sleep(long millis)
        2、static void sleep(long millis,int nanos)
sleep()与yield()比较
        1、sleep()会让线程进入阻塞状态、而yield()会让线程进入就绪状态
        2、sleep()给其他线程运行机会,不理会其他线程的优先级别,yield()考虑线程的优先级,只会给优先级或者优先级更高的线程执行机会。

2、 等待线程

Thread类提供了等待的方法,可以让调用方等待该线程直至他死亡。
        * void join()
        * void join(long millis)
        * void join(long millis , int nanos)
    public static void main(String[] args) throws InterruptedException {
        newJoinThread();//Join演示

    }

    //Join演示
    private static class JoinThreadTask implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 20; i ++){
                System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
            }
        }
    }

    //创建线程
    public static void newJoinThread() throws InterruptedException {
        //没有Join处理子线程
        new Thread(new JoinThreadTask()).start();
        //主线程
        for (int i = 0; i < 20; i ++){
            System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
            if (i == 10){
                //使用Join方法的子线程
                Thread thread = new Thread(new JoinThreadTask());
                thread.start();
                thread.join();
            }

        }
    }

3、后台线程

* 后台线程、也就守护线程、或者精灵线程。他是在后台运行的,他的任务是为其他线程提供服务的。如果所有的前台线程都死亡,则后台线程会自动死亡
* 线程默认是前台线程,Thread类提供如下方法设置后台线程
        * void() setDaemon(boolean on)
        * boolean inDaemon()
    public static void main(String[] args) throws InterruptedException {

        newDaemoThread();//后台线程

    }



    //后台线程
    private static class DaemonThreadTask implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 200; i ++){
                System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
            }
        }
    }

    public static void newDaemoThread(){
        //创建一个后台线程
        Thread thread = new Thread(new DaemonThreadTask());
        thread.setDaemon(true);//设置后台线程
        thread.start();

        //主线程
        for (int i = 0; i < 20; i ++){
            System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名

        }

    }

4、线程优先级

* 线程运行时候拥有优先级,优先级高的线程则拥有较多的运行机会
* 线程默认的优先级与它的父线程相同,而主线程具有普通的优先级
* Thread类提供如下成员处理优先级
        * static int MAX_PRIORITY;
        * static int MIN_PRIORITY;
        * static int NORM_PRIORITY;
        * int getPriority();
        * void setPriority(int newPriority);
    public static void main(String[] args) throws InterruptedException {

        newThreadPriority();//优先级
    }

    //优先级
    private static class priorityThreadTask implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 200; i ++){
                System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
            }
        }
    }

    public static void newThreadPriority(){
        //创建两个线程用于比较优先级
        Thread thread1 = new Thread(new priorityThreadTask());
        Thread thread2 = new Thread(new priorityThreadTask());
        thread1.setPriority(Thread.MAX_PRIORITY);//优先级设置最大
        thread1.start();
        thread2.setPriority(Thread.MIN_PRIORITY);//优先级设置最小
        thread2.start();


    }

五、有关线程安全

 1.线程不安全示例

首先展示一个线程不安全的示例,数据共享场景,以一个假话的抢票系统为例

   /**
     * 线程安全
     */
    //模拟抢票系统
    public static void main(String[] args){
        Ticket ticket = new Ticket(100);//初始化100张票
        //售卖系统
        buyTask task = new buyTask(ticket);
        new Thread(task,"Tom").start();//三者共享同一个数据,共享数据场景
        new Thread(task,"John").start();
        new Thread(task,"Lily").start();
        new Thread(task,"张三").start();
        new Thread(task,"李四").start();
    }
    //设计票数
    private static class Ticket{

        private int amount;//票数
        public Ticket(int amount){//构造器初始化票数
            this.amount = amount;
        }

        //访问数量的方法
        public int getAmount(){
            return amount;
        }

        public void buy(int amount)  {
            if (this.amount < amount){
                throw new IllegalArgumentException("余量不足");
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.amount -= amount;
            String name = Thread.currentThread().getName();
            System.out.println(name + "购买成功,剩余票数" + this.amount);
        }

    }

    //卖票
    private static class buyTask  implements Runnable{

        private Ticket ticket;

        public buyTask(Ticket ticket){//构造器
            this.ticket = ticket;
        }

        @Override
        public void run() {
            ticket.buy(1);//每次只能买一张
        }
    }

运行结果,共享数据出现重复

张三购买成功,剩余票数96
Tom购买成功,剩余票数96
Lily购买成功,剩余票数96
李四购买成功,剩余票数96
John购买成功,剩余票数96

2、解决线程安全方法

01、同步代码块

使用synchronized对某一段代码加锁

 public static void main(String[] args) {
        Ticket ticket = new Ticket(100);//初始化100张票
        //售卖系统
        buyTask task = new buyTask(ticket);
        new Thread(task, "Tom").start();//三者共享同一个数据,共享数据场景
        new Thread(task, "John").start();
        new Thread(task, "Lily").start();
        new Thread(task, "张三").start();
        new Thread(task, "李四").start();
    }

    //设计票数
    private static class Ticket {

        private int amount;//票数

        public Ticket(int amount) {//构造器初始化票数
            this.amount = amount;
        }

        //访问数量的方法
        public int getAmount() {
            return amount;
        }

        public void buy(int amount) {
            if (this.amount < amount) {
                throw new IllegalArgumentException("余量不足");
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //对之下代码加锁,解决数据共享时候的BUG
            synchronized (this){
                //通过校验成功购票
                this.amount -= amount;
                String name = Thread.currentThread().getName();
                System.out.println(name + "购买成功,剩余票数" + this.amount);
            }
        }

    }

    //卖票
    private static class buyTask implements Runnable {

        private Ticket ticket;

        public buyTask(Ticket ticket) {//构造器
            this.ticket = ticket;
        }

        @Override
        public void run() {
            ticket.buy(1);//每次只能买一张
        }
    }

运行结果

John购买成功,剩余票数99
李四购买成功,剩余票数98
Tom购买成功,剩余票数97
张三购买成功,剩余票数96
Lily购买成功,剩余票数95

Process finished with exit code 0

02、同步方法块

在方法上加synchronized,此方法比同步代码块差,因为加锁范围增大大,加锁后性能降低
   public static void main(String[] args) {
        Ticket ticket = new Ticket(100);//初始化100张票
        //售卖系统
        buyTask task = new buyTask(ticket);
        new Thread(task, "Tom").start();//三者共享同一个数据,共享数据场景
        new Thread(task, "John").start();
        new Thread(task, "Lily").start();
        new Thread(task, "张三").start();
        new Thread(task, "李四").start();
    }

    //设计票数
    private static class Ticket {

        private int amount;//票数

        public Ticket(int amount) {//构造器初始化票数
            this.amount = amount;
        }

        //访问数量的方法
        public int getAmount() {
            return amount;
        }

        public synchronized void buy(int amount) {
            if (this.amount < amount) {
                throw new IllegalArgumentException("余量不足");
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.amount -= amount;
            String name = Thread.currentThread().getName();
            System.out.println(name + "购买成功,剩余票数" + this.amount);
        }

    }

    //卖票
    private static class buyTask implements Runnable {

        private Ticket ticket;

        public buyTask(Ticket ticket) {//构造器
            this.ticket = ticket;
        }

        @Override
        public void run() {
            ticket.buy(1);//每次只能买一张
        }
    }

运行结果

John购买成功,剩余票数99
李四购买成功,剩余票数98
Tom购买成功,剩余票数97
张三购买成功,剩余票数96
Lily购买成功,剩余票数95

Process finished with exit code 0

03、同步锁

最优解,灵活性高,可以实现读写锁分离
public static void main(String[] args) {
        Ticket ticket = new Ticket(100);//初始化100张票
        //售卖系统
        buyTask task = new buyTask(ticket);
        new Thread(task, "Tom").start();//三者共享同一个数据,共享数据场景
        new Thread(task, "John").start();
        new Thread(task, "Lily").start();
        new Thread(task, "张三").start();
        new Thread(task, "李四").start();
    }

    //设计票数
    private static class Ticket {
        private Lock lock = new ReentrantLock();//实例化锁对象

        private int amount;//票数

        public Ticket(int amount) {//构造器初始化票数
            this.amount = amount;
        }

        //访问数量的方法
        public int getAmount() {
            return amount;
        }

        public void buy(int amount) {
            if (this.amount < amount) {
                throw new IllegalArgumentException("余量不足");
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            lock.lock();//表示在此处加锁
            try{
                this.amount -= amount;
                String name = Thread.currentThread().getName();
                System.out.println(name + "购买成功,剩余票数" + this.amount);
            }finally {
                lock.unlock();//解锁
            }
        }

    }

    //卖票
    private static class buyTask implements Runnable {

        private Ticket ticket;

        public buyTask(Ticket ticket) {//构造器
            this.ticket = ticket;
        }

        @Override
        public void run() {
            ticket.buy(1);//每次只能买一张
        }
    }

运行结果

John购买成功,剩余票数99
李四购买成功,剩余票数98
Tom购买成功,剩余票数97
张三购买成功,剩余票数96
Lily购买成功,剩余票数95

Process finished with exit code 0

*  以上三种方法是通过加锁方式实现共享数据时候的线程安全问题

六、死锁

当两个线程互相等待对方释放同步监视器时候会发生死锁。


1、死锁示例:

 public static void main(String[] args) {
        String a = "A";
        String b = "B";
        new Thread(new FristTask(a,b)).start();
        new Thread(new SercodTask(a,b)).start();
    }
    private static class FristTask implements Runnable{

        private Object a;
        private Object b;
        public FristTask(Object a,Object b){//构造器初始化a,b
            this.a = a;
            this.b = b;
        }
        @Override
        public void run() {
            synchronized (a){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b){
                    System.out.println("First");
                }

            }
        }
    }
    private static class SercodTask implements Runnable{

        private Object a;
        private Object b;
        public SercodTask(Object a,Object b){//构造器初始化a,b
            this.a = a;
            this.b = b;
        }
        @Override
        public void run() {
            synchronized (b){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a){
                    System.out.println("First");
                }

            }
        }
    }

程序一直运行,不会输出

2、解决方法

* 避免多次死锁 : 尽量避免同一个线程对多个同步监视器进行锁定

* 按相同的顺序加锁 : 如果多个线程需要对多同步监视器加锁,则应该保证他们以相同的顺序请求加锁

* 使用可以超时释放的锁 : 调用Lock()对象的tryLock(time,unit)方法,当超过指定的时间后他会自动释放锁

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值