多线程编程实现方式

本文介绍了多线程与进程的区别,强调了多线程在利用CPU空闲时间处理任务上的优势。接着,详细讲解了两种实现Java多线程编程的方式:继承Thread类和实现Runnable接口,并通过示例展示了线程间数据共享可能遇到的非线程安全问题及解决方案,如使用synchronized关键字实现线程同步。

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

区分多线程与进程:

  • 进程是受OS管理的基本运行单元,也就是“一次程序的执行”;
  • 多线程则可以理解成在进程中独立运行的子任务,他们可以同时运行。

使用多线程的优点:

直观地,比如Windows(多任务操作系统)可以最大限度地使用CPU的空闲时间来处理其他任务,CPU在不同的任务之间不停地切换,由于切换的速度很快,我们就觉得这些任务好像是在同时运行。So,使用多线程可以在同一时间内运行更多不同类型的任务

有一点需要注意的是:
线程被调用的顺序是随机的,与代码顺序无关。

实现多线程编程的方式:

  1. 继承Thread类
  2. 实现Runnable接口
public class Thread implements Runnable

Thread类实现了Runnable接口,他们之间具有多态关系,其实,继承Thread类创建新线程时,有不支持多继承的局限(因为Java语言的特点是单根继承),所以为了支持多继承,可以实现Runnable接口,但两种方法没有本质的区别。

Method 1 继承Thread类:
package com.multithread.www;

public class MyThread extends Thread{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		System.out.println("MyThread");
	}
}

运行类代码:

package test;
import com.multithread.www.MyThread;

public class Run {
	public static void main(String[] args) {
		MyThread mythread = new MyThread();
		mythread.start();
		System.out.println("Finished!");
	}
}

运行结果:
运行结果
运行结果显示:run方法执行较晚,也证实了前面所说的线程被调用的顺序是随机的,与代码顺序无关
接下来展现线程具有的随机性,

package mythread;

public class MyThread extends Thread {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		//super.run();
		try{
			for(int i = 0; i < 10; i++) {
				int time = (int) (Math.random() * 1000);
				Thread.sleep(time);
				System.out.println("run=" + Thread.currentThread().getName());
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
package test;

import mythread.MyThread;

public class Test {
	public static void main(String[] args) {
		try {
			MyThread thread = new MyThread();
			thread.setName("mythread");
			thread.start();  // 通知“线程规划器”此线程已准备就绪,等待调用此线程的run方法
			for(int i = 0; i < 10; i++) {
				int time = (int) (Math.random() * 1000);
				Thread.sleep(time);
				System.out.println("main=" + Thread.currentThread().getName());
			}
		}catch (InterruptedException e){
			e.printStackTrace();
		}
	}
}

运行结果:
运行结果
多次执行结果不同,因为CPU以不确定的方式(随机的时间)调用线程中的run方法,与start()方法顺序无关。

Method 2 实现Runnable接口:

如果要创建的线程已经有一个父类了,由于Java不支持多继承,就不能继承Thread类了,就需要implements Runnable接口创建新的线程。

package myrunnable;

public class MyRunnable implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("运行中...");
	}
}
package test;

import myrunnable.MyRunnable;

public class Run {
	public static void main(String[] args) {
		Runnable runnable = new MyRunnable();
		Thread thread = new Thread(runnable);  // Thread类的带参数构造函数,传递Runnanle接口
		thread.start();
		System.out.println("运行结束!");
	}
}

运行结果:
在这里插入图片描述
细想一下,Thread类实现了Runnable接口,意味着,Thread的构造函数Thread(Runnable target)不仅可以传入Runnable接口的对象,还可以传入一个Thread类的对象,从而实现了将一个Thread对象中的run方法交给其他线程进行调用。

线程间数据共享

自定义线程类中的实例变量针对其他线程有共享与不共享之分。

1. 不共享数据
package multithread_t3;

public class MyThread extends Thread {
	private int count = 5;  // 不共享变量
	public MyThread(String name) {
		super();
		this.setName(name);
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		while(count > 0) {
			count--;
			System.out.println("由" + this.currentThread().getName() + "计算,count=" + count);
		}
	}
}
package test;

import multithread_t3.MyThread;

public class Run {
	public static void main(String[] args) {
		MyThread a = new MyThread("A");
		MyThread b = new MyThread("B");
		MyThread c = new MyThread("C");
		a.start();
		b.start();
		c.start();
	}
}

运行结果:
在这里插入图片描述
三个线程有各自的count变量,各自减少count变量的值。

2. 共享数据

如果想多个线程共同完成count减少的操作,应如何?
多个线程应共享同一个变量count!

package t4;

public class MyThread extends Thread{
	private int count = 5;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		count--;
		System.out.println("由" + this.currentThread().getName() + "计算,count=" + count);
	}
}
package test;

import t4.MyThread;

public class Run {
	public static void main(String[] args) {
		MyThread mythread = new MyThread();
		Thread a = new Thread(mythread, "A");  // Thread类的构造函数Thread(ThreadGroup group, String name)
		Thread b = new Thread(mythread, "B");
		Thread c = new Thread(mythread, "C");
		Thread d = new Thread(mythread, "D");
		Thread e = new Thread(mythread, "E");
		a.start();
		b.start();
		c.start();
		d.start();
		e.start();
	}
}

运行结果:
在这里插入图片描述
完成了五个线程共同减少count变量的值,但线程A和B打印的count值都是3,这就说明A和B同时对count进行了操作,“非线程安全”也由此产生。

非线程安全:
非线程安全主要是指多个线程对同一对象中的同一个实例变量进行操作时会出现‘值被改变,值不同步’的情况,进而影响程序的执行流程

分析原因:
在某些JVM中,i–操作分为三步:

  1. 获取i值
  2. 计算i-1
  3. 对i赋值

所以,在i–过程中,如果多个线程同时访问i,一定会出现非线程安全问题。
修改代码:

package t4;

public class MyThread extends Thread{
	private int count = 5;

	@Override
	synchronized public void run() {  // 实现线程同步
		// TODO Auto-generated method stub
		super.run();
		count--;
		System.out.println("由" + this.currentThread().getName() + "计算,count=" + count);
	}
	
}

运行结果:
在这里插入图片描述
在run()前加入synchronized关键字,实现了多线程同步,也就是按顺序排队的方式对count减1。
synchronized关键字可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
最后,实现一下非线程安全的环境,

package controller;

// 本类模拟成一个Servlet组件
public class LoginServlet {
	private static String usernameRef;
	private static String passwordRef;
	public static void doPost(String username, String password) {
		try {
			usernameRef = username;
			if(username.equals("a")) {
				Thread.sleep(5000);
			}
			passwordRef = password;
			System.out.println("username=" + usernameRef + "  password=" + passwordRef);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}

package extthread;

import controller.LoginServlet;

public class ALogin extends Thread{

	@Override
	public void run() {
		LoginServlet.doPost("a", "aa");
	}
	
}

package extthread;

import controller.LoginServlet;

public class BLogin extends Thread{

	@Override
	public void run() {
		LoginServlet.doPost("b", "bb");
	}
	
}

package test;

import extthread.ALogin;
import extthread.BLogin;

public class Run {
	public static void main(String[] args) {
		ALogin a = new ALogin();
		a.start();
		BLogin b = new BLogin();
		b.start();
	}
}

运行结果:
在这里插入图片描述
加入synchronized关键字后,运行结果:

synchronized public static void doPost(String username, String password) 

在这里插入图片描述

Reference:

高洪岩,《Java多线程编程核心技术》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值