c 多线程端口扫描程序_Java多线程学习(一)——多线程基础介绍

本文介绍了进程和线程的概念、区别及多线程的运行方式。阐述了Java中实现多线程的两种主要方法,即继承Thread类和实现Runnable接口。同时,分析了线程安全问题,包括不共享数据和共享数据的情况,以及共享数据时线程不安全的原因和解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0b03beafeef139b7adcc9b13713e4363.png

可到简书获取本文代码github下载地址以及获取更多文章:https://www.jianshu.com/p/858568a01670

1、进程和线程

1.1 进程

进程是操作系统运行程序的基本单位,是一次程序的执行。简单来说一个进程就是一个运行中的程序。

1.2 线程

线程可以认为是在进程中独立运行的子任务。一个进程会有多个线程。

1.3 进程和线程区别

进程和线程最大区别就是,各个进程是独立的,而线程却不一定,同一进程中的线程可能是相互影响的。进程属于操作心痛范围的,同一时间会运行多个程序,每个进程上的又会有多个线程在执行同个或不同的任务。

1.4 多线程

多线程就是多个线程同时运行或交替运行。单核CPU的话是顺序执行,也就是交替运行。多核CPU的话,因为每个CPU有自己的运算器,所以在多个CPU中可以同时运行。

2、使用多线程

在Java的JDK开发包中,实现多线程主要有两种,一种是继承Thread类,另一种是实现Runnable接口。其实还有很多事项多线程方法,例如:使用ExecutorService、Callable、Future实现由返回结果的多线程,这是通过线程池创建的任务,可以参考《一篇搞懂线程池》。现在我们只来具体来讲一下前两种方法的使用。

2.1 继承Thread类

在创建多线程前,我们先来看一下Thread的类结构图:

aaaf4a8b426be5647a7fe468ab2fc3e2.png

可以看出Thread实现了Runnable接口,其实Runnable和Thread的区别就在于继承Thread创建多线程不支持多继承,为了多继承完全可以使用Runnable替换,两者创建的线程本质上没有区别。@FunctionalInterface是Java8的函数式接口,方便lambda表达式使用。

我们创建一个Mythread.java继承Thread,重写run方法:

package main.java.com.xiaosen.Mythread;/** * @author xiaosen * @date 2019/2/24 10:36 * @description */public class MyThread extends Thread{ @Override public void run() { super.run(); System.out.println("this is MyThread"); }}

运行类的代码如下:

import main.java.com.xiaosen.Mythread.MyThread;public class Main { public static void main(String[] args) { // 继承Thread MyThread myThread = new MyThread(); myThread.start(); // lambda表达式的使用 new Thread(() -> System.out.println("this is lambda thread")).start(); System.out.println("Hello World!"); }}

运行结果如下:

this is MyThreadHello World!this is lambda thread

这里一低昂要注意myThread调用的是start()方法,如果调用mythread.run()那么就是单纯的方法调用,而不是创建线程去执行。关于lambda表达式只给出使用方法,暂不做介绍。

从运行结果可以看见"Hello World!"的输出在两个线程中间,说明使用多线程和代码的顺序无关。CPU会以随机的时间来调用线程中的run方法。

37e4af2f74c641ae77542be804849e5d.png

2.3 实现Runnable接口

创建MyRunnable实现Runnable接口:

package main.java.com.xiaosen.myrunnable;/** * @author xiaosen * @date 2019/2/24 11:20 * @description */public class MyRunnable implements Runnable{ @Override public void run() { System.out.println("run MyRunnable"); }}

main方法使用:

// 实现Runnable MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); System.out.println("Hello World!");

输出内容:

Hello World!run MyRunnable

使用Runnable方法需要用到Thread的构造方法。

3、线程安全

自定义的实例变量有共享和不共享之分,在多线程交互的时候很重要。

3.1 不共享数据

创建一个类继承Thread:

public class NotShareThread extends Thread{ private int count = 5; public NotShareThread(String name){ super(); this.setName(name); } @Override public void run() { super.run(); while (count>0){ count--; System.out.println("线程" + this.currentThread().getName() + ":count=" + count); } }} public static void main(String[] args){ NotShareThread notShareThread1 = new NotShareThread("A"); NotShareThread notShareThread2 = new NotShareThread("B"); NotShareThread notShareThread3 = new NotShareThread("C"); notShareThread1.start(); notShareThread2.start(); notShareThread3.start(); }

执行结果:

线程A:count=4线程B:count=4线程C:count=4线程B:count=3线程A:count=3线程A:count=2线程A:count=1线程B:count=2线程C:count=3线程B:count=1线程A:count=0线程B:count=0线程C:count=2线程C:count=1线程C:count=0

从输出结果可以看到每一个线程都有各自的count变量,彼此变量不共享。

3.2 共享数据

共享数据就是多个线程同时访问同一个变量,比如买火车票,多个线程同时操作剩余票数。

3.2.1 线程不安全

public class ShareThread extends Thread { private int count = 5; @Override public void run() { super.run(); count--; System.out.println("线程" + this.currentThread().getName() + ":count=" + count); } public static void main(String[] args){ ShareThread shareThread = new ShareThread(); Thread a = new Thread(shareThread, "A"); Thread b = new Thread(shareThread, "B"); Thread c = new Thread(shareThread, "C"); Thread d = new Thread(shareThread, "D"); Thread e = new Thread(shareThread, "E"); a.start(); b.start(); c.start(); d.start(); e.start(); }}

输出:

线程B:count=2线程E:count=0线程D:count=1线程C:count=2线程A:count=2

可以发现A,B,C三个线程的值都是2,产生非线程安全情况,我们应该避免这种情况发生。这是因为在执行i--操作分以下几步:

  1. 获取原有i值;
  2. 计算i-1;
  3. 堆i赋值。

在这三步中,如果有多线程同时访问,就会出现线程不安全情况。

3.2.2 线程安全

我们来看看线程安全的代码:

@Override public void run() { super.run(); synchronized (ShareThread.class){ count--; System.out.println("线程" + this.currentThread().getName() + ":count=" + count); } }

我们加上synchronized代码块加锁,那么只有拿到所锁的线程才可以执行代码块的内容,没拿到则不断尝试获取锁,知道拿到为止。

9222ffe4220f6779be9e4bd388075c4a.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值