多线程
1.程序、进程、线程
-
在操作系统中运行的程序就是进程;
-
一个进程可以有多个线程;
-
程序是指令和数据的有序集合,是一个静态的概念;
-
而进程则是执行程序的一次执行过程,是一个动态的概念;
-
进程是系统资源分配的单位
-
线程是CPU调度和执行的单位
-
真正的多线程是指多个cpu,即多核
理解:进程相当于一个类,类中有多个方法(即多个线程),main()方法即(主线程),gc线程。
- main()方法即(主线程)为系统的入口,用于执行整个程序;
- 线程的运行是由调度器安排调度,不能人为干预;
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如cpu的调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
2.线程的创建
-
三种创建方式
-
Thread class
- 继承Thread类
-
Runnable接口
- 实现Runnable接口
-
Callable接口
- 实现Callable接口
-
3.Thread
- 自定义类继承Thread
- 重写run()方法;
- 创建对象,调用start()开启线程
import javax.xml.transform.Source;
public class TestThread01 extends Thread{
//继承Thread类
//总结:线程开启不一定立即执行,有CPU调度执行
@Override
public void run() {
//run()方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("**********-4564646465");
}
}
//main线程,即主线程
public static void main(String[] args) {
TestThread01 testThread01 = new TestThread01();
//testThread01.start();//线程开启,交替(类似同时)执行,电脑单核只有一个cpu
testThread01.run();//这个在前,先执行run();
for (int i = 0; i < 20; i++) {
System.out.println("7554574545*********");
}
}
}
多线程实现多张图片同时下载
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class TestThread02 extends Thread{
private String url;
private String name;
public TestThread02(String url,String name){
this.url=url;
this.name=name;
}
public TestThread02(){
}
@Override
public void run() {
//下载线程
WebDownloader webDownloader = new WebDownloader();
try {
webDownloader.downloader(url,name);
System.out.println(name);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TestThread02 testThread02 = new TestThread02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603037822768&di=5f0cc79b7c104781de8cea48c7d8b1ca&imgtype=0&src=http%3A%2F%2Fattach.bbs.miui.com%2Fforum%2Fmonth_1012%2F10120514509c7244b23f4a2fa5.jpg","a.jpg");
TestThread02 testThread03 = new TestThread02("https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00720-1746.jpg","a1.jpg");
TestThread02 testThread04 = new TestThread02("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2441956749,4275399915&fm=26&gp=0.jpg","a2.jpg");
testThread02.start();
testThread03.start();
testThread04.start();
}
}
//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name) throws IOException {
FileUtils.copyURLToFile(new URL(url),new File(name));
}
}
4.Runnable
推荐使用Runnable对象,因为Java单继承的局限性
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
public class TestRunnable01 implements Runnable{
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("*******************");
}
}
public static void main(String[] args) {
TestRunnable01 testRunnable01 = new TestRunnable01();
new Thread(testRunnable01).start();
}
}
5.小结
-
继承Thread
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
-
实现Runnable接口
- 实现接口Runnable具备多线程能力
- 启动线程:传入目标对象+Thread对象。start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
多个线程操作同一个资源问题
//多个线程操作同一个资源
//模拟买票
//发现问题:多个线程操作同一个人资源的情况下,线程不安全,数据紊乱。
public class TestRunnable02 implements Runnable{
private int ticket=10;
public void run() {
while (true){
if (ticket<=0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticket--+"票");
}
}
public static void main(String[] args) {
TestRunnable02 runnable02=new TestRunnable02();
new Thread(runnable02,"小鹏").start();
new Thread(runnable02,"小强").start();
new Thread(runnable02,"小慧").start();
}
}
6.实例:龟兔赛跑
import org.omg.Messaging.SyncScopeHelper;
public class Race implements Runnable{
private static String winner;
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉
if (Thread.currentThread().getName().equals("兔子")&&i%50==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断100步
if (win(i)){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
}
}
//判断胜利者
public boolean win(int step){
if (winner!=null){
return true;
}
if (step>=100){
winner=Thread.currentThread().getName();
System.out.println("胜利者是"+winner);
}
return false;
}
public static void main(String[] args) {
Race race=new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}