多线程并发的好处、问题以及synchronized关键字的使用方法

多线程并发的好处、问题以及synchronized关键字的使用方法

一、线程简介

什么线程呢?简单来说线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程中可以并发多个线程,每条线程并行执行不同的任务。

二、多线程的好处

那我们为什么要使用多线程呢?
这里有个例子能很好地说明

 Scanner s = new Scanner(System.in);
        for (int i = 0; i < 100; i++) {
            System.out.println("A工作了"+i);
            if(i==50){
                System.out.println("A工作了一般,是否继续?");
                String ans = s.next();
                if(ans == "y"){
                    continue;
                }
                else{
                System.out.println("A下班了");
                    break;
                }
            }
        }
        for (int i = 0; i < 100; i++) {
            System.out.println("B工作了"+i);
        }

上述代码中,首先执行A,在A执行了一半时询问是否继续执行。在这段时间B一直处于等待状态,如果A一直不回应那么B永远也无法工作。

在这里插入图片描述

在实际情况中也有很多类似的例子,如果你在使用电脑连接微信或者QQ网速比较慢,在单线程的情况下,你只能盯着服务器的连接什么也做不了,就像上述代码中的小B一样。

如果我们使用两个线程将A B分开工作

public class test07 {
    public static void main(String[] args) {
        ATask aTask = new ATask();
        BTask bTask = new BTask();
        aTask.start();
        bTask.start();
    }
}

class ATask extends Thread{
    @Override
    public void run() {
        Scanner s = new Scanner(System.in);
        for (int i = 0; i < 100; i++) {
            System.out.println("A工作了" + i);
            if (i == 50) {
                System.out.println("A工作了一般,是否继续?");
                int ans = s.nextInt();
                if (ans == 111) {
                    continue;
                } else {
                    System.out.println("下班啦");
                    break;
                }
            }
        }
    }
}
class BTask extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("B工作了" + i);
        }
    }
}

这样AB的工作就互不相关了,无论A那里停多久B都继续执行他的任务
在这里插入图片描述
在这里插入图片描述
总的来说,多线程的优点有以下几个
(1)多线程技术使程序的响应速度更快 ,因为用户界面可以在进行其它工作的同时一直处于活动状态;

(2)占用大量处理时间的任务使用多线程可以提高CPU利用率,即占用大量处理时间的任务可以定期将处理器时间让给其它任务;

(3)多线程可以分别设置优先级以优化性能。

三、多线程的问题

多线程有许多优点相应的也会带来一些问题,通常多线程在工作时会访问各种各样的数据,当两个线程同时获取了同一个成员变量进行处理时,该数据就可能出现问题。例如仓库中有三箱货物,线程一和线程二同时获取了三箱货物分别用了一箱,理论上还有一箱货物,实际结果我们可以测试一下

public class test07 {
    public static int productNum= 3;
    public static void main(String[] args) {
        System.out.println("剩余产品还有"+productNum+"箱");
        ATask aTask = new ATask();
        BTask bTask = new BTask();
        aTask.start();
        bTask.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("剩余产品还有"+productNum+"箱");
    }
}

class ATask extends Thread{
    @Override
    public void run() {
       int num = test07.productNum;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num--;
        System.out.println("任务A消耗了一箱产品");
        test07.productNum=num;
    }
}
class BTask extends Thread{
    @Override
    public void run() {
        int num = test07.productNum;
        try {
            Thread.sleep(1100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num--;
        System.out.println("任务B消耗了一箱产品");
        test07.productNum=num;
    }
}

在这里插入图片描述
我们发现上述代码的结果和理论结果不同,是因为任务A和任务B在消耗产品之前拿到的产品数量都为3,所以两个线程消耗完产品后都认为剩余两箱才有以下结果。
也就是说产品数量同时被两个线程拿到是不安全的
那我们是否有一种方法可以每次只让一个线程拿到数据呢?

四、synchronized关键字

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

被synchronized关键词修饰的对象简单来说会加上一把锁,当一个线程获取该对象时,就将该变量上锁。后续线程就不能获取该变量,直到上一个线程使用完该变量释放掉手中的锁

public class test07 {
    public static Integer productNum= 3;
    public static void main(String[] args) {
        System.out.println("剩余产品还有"+productNum+"箱");
        ATask aTask = new ATask();
        BTask bTask = new BTask();
        aTask.start();
        bTask.start();
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("剩余产品还有"+productNum+"箱");
    }
}

class ATask extends Thread{
    @Override
    public void run() {
        synchronized(test07.productNum){
            int num = test07.productNum;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            num--;
            System.out.println("任务A消耗了一箱产品");
            test07.productNum=num;
        }
    }
}
class BTask extends Thread{
    @Override
    public void  run() {
        synchronized(test07.productNum){
            int num = test07.productNum;
            try {
                Thread.sleep(1100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            num--;
            System.out.println("任务B消耗了一箱产品");
            test07.productNum=num;
        }
    }
}

加入synchronized关键词后,结果如下
在这里插入图片描述
以上就是对于多线程并发总结的部分优点、问题和解决方法。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值