JAVA 学习并行与并发、线程(Thread)、多线程、线程安全问题、线程同步( synchronized、Lock锁)


个人博客文章地址

一、并发与并行

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

并发
个人理解是在同一时间段内,多个程序在人类看起来是同时执行的,但在单核处理器中,其实他们相互交替的运行,而因为他们的交替运行速度非常快(故看起来是在同时运行)如图:
在这里插入图片描述
并行
并行这只能在多核处理器中存在,顾名思义,多个程序可以同时执行,大大提高了运行效率,如图:
在这里插入图片描述

二、线程与进程

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
即:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

三、JAVA线程(Thread)的使用

使用方法一:继承Thread类

目录:
在这里插入图片描述
UseThread类:继承Thread类,并且重写run方法

public class UseThread extends Thread{
    @Override
    public void run() {
        System.out.print("我是一个线程");
    }
}

ThreadTest类:用作测试,实例化UseThread,并且调用start()方法即可

public class ThreadTest {
    public static void main(String[] args) {
        Thread test = new UseThread();
        test.start();
    }
}

使用方法二:实现Runnable接口

UseThread类:实现Runnable接口,并且重写run方法

public class UseThread implements Runnable{
    @Override
    public void run() {
        System.out.print("我是一个线程");
    }
}

ThreadTest类:用作测试,实例化UseThread类,新建一个Thread对象,传入UseThread对象,最后调用start()方法即可

public class ThreadTest {
    public static void main(String[] args) {
        Runnable test = new UseThread();

        Thread hello = new Thread(test);

        hello.start();
    }
}

运行效果:
在这里插入图片描述

两种方法的区别大部分是继承和接口的区别,要注意的是实现Runnable的线程能放入线程池中,但是继承Thread的线程不可以所以用实现Runnable接口来使用线程要好一些。

三、多线程使用

其实在上面的例子中已经是一个多线程程序了,main方法已经是一个线程,通过实例化线程对象,并且调用了start()方法就会开启一个新的线程(同时会给该线程分配一个新的栈空间),如果直接调用run方法则会在当前线程运行,我们可以通过Thread.currentThread().getName()方式查看当前运行的线程名字
多线程优点:提高了运行效率,还要挺重要的一点是,各个线程在执行的时候,彼此之间不会产生影响(没有线程通信的情况),即其中一个线程有bug报错,一般情况下其它线程也可以照常运行

main:
在这里插入图片描述
示例对象:
在这里插入图片描述
结果:
在这里插入图片描述

线程安全问题

在JAVA多线程中,各个同优先级的线程是通过互相竞争CPU的使用权来运行的,类似开头的并发操作,各个线程会相互替换在CPU(单核)中运行的,而这种发生就有可能引发线程安全问题,下面用例子说明

UseThread类: 让该线程从100开始递减,直到输出sum 的值是0为止,其中使用Thread.sleep(200)方法是为了增大出现线程安全问题的概率(为了演示),这个后面说明原因

public class UseThread implements Runnable{
    private int sum = 100;
    @Override
    public void run() {
        while (true){
            if(sum > 0){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             
                System.out.println("我是线程" + Thread.currentThread().getName() + "当前sum为" + sum);
				sum --;
            }
            else
                break;
        }

    }
}

ThreadTest测试类: 新建两个线程,但是运行同一个线程对象,用于测试

public class ThreadTest {
    public static void main(String[] args) {
        Runnable test = new UseThread();

        Thread hello = new Thread(test);
        Thread world = new Thread(test);

        hello.start();
        world.start();
    }
}

结果出现问题:
相同的sum值
在这里插入图片描述

问题出现原因: 线程间相互抢占CPU运行所导致(sleep是为了防止运行速度太快,太快会导致可能看不到这种情况)线程1来不及执行sum- -,就被线程2抢占了CPU运行

四、线程同步

对于出现线程安全问题,我们可以采用线程同步方式

1.同步代码块

synchronized(同步锁(任意类型)){
可能会产生线程安全问题的代码
}

实现: 随便加入一个任意类型的锁

public class UseThread implements Runnable{
    private int sum = 100;
    String str = "Lock";
    @Override
    public void run() {
        while (true){
            synchronized (str){ //同步代码块
                if(sum > 0){
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("我是线程" + Thread.currentThread().getName() + "当前sum为" + sum);
                    sum --;

                }
                else
                    break;
            }
        }
    }
}

2.同步方法

public synchronized void method(){
可能会产生线程安全问题的代码
}

实现: 把可能会产生线程安全问题的代码放到同步方法里,然后调用即可

public class UseThread implements Runnable{
    private int sum = 100;

    //同步方法
    public synchronized void method(){
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("我是线程" + Thread.currentThread().getName() + "当前sum为" + sum);
        sum --;
    }
    @Override
    public void run() {
        while (true){
                if(sum > 0){
                    method();
                }
                else
                    break;
        }

    }
}

3. Lock锁

public void lock() :加同步锁。
public void unlock() :释放同步锁。

实现:

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

public class UseThread implements Runnable{
    private int sum = 100;
    Lock lock = new ReentrantLock();//同步锁

    @Override
    public void run() {
        while (true){
                lock.lock();
                if(sum > 0){
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("我是线程" + Thread.currentThread().getName() + "当前sum为" + sum);
                    sum --;

                }
                else
                    break;
            }
                lock.unlock();
    }
}

以上三种方法均可实现线程同步。还有线程通信和线程池的使用,学得一般,我就不丢人现眼了~~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值