Java学习总结三十八:多线程(一)

本文深入探讨Java多线程的概念,包括线程的创建方法、线程间资源竞争及并发控制,通过具体示例讲解继承Thread类、实现Runnable接口和Callable接口三种线程创建方式。

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

线程简介

  • 方法间的调用:普通方法调用,从哪里来到哪里去,闭合的一条路径
  • 多线程使用:开辟了多条路径

在这里插入图片描述
在这里插入图片描述
核心概念:

  • 线程就是独立的执行路径;
  • 在程序运行时,即使没有自己创建线程,后台也会存在多个线程,如gc线程、主线程;
  • main()称之为主线程,为系统的入口点,用于执行整个程序;
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的;
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销;
  • 每个线程在自己的工作内存交互,加载和存储主内存控制不当会造成数据不一致。

线程的创建

1,继承Thread类
thread

线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。

当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某些指定类的名为main的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况:

  • 已经调用了Runtime类的exit方法,并且安全管理器已经允许进行退出操作。
  • 所有不是守护进程线程的线程都已经死亡,无论是从调用返回到run方法还是抛出超出run方法的run 。

创建一个新的执行线程有两种方法。 一个是将一个类声明为Thread的子类。 这个子类应该重写run类的方法Thread 。 然后可以分配并启动子类的实例。

class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }

然后,以下代码将创建一个线程并启动它运行:

PrimeThread p = new PrimeThread(143);
	p.start();

示例:

package com.jsp.thread;

/**
 * 创建线程方式1
 * 1,创建:继承Thread+重写run
 * @author Jsp
 *
 */
public class StartThread extends Thread {
	/**
	 * 线程入口
	 */
	@Override
	public void run() {
		for(int i=0;i<20;i++){
			System.out.println("一边听歌");
		}
	}
	
	public static void main(String[] args) {
		//创建子类对象
		StartThread st = new StartThread();
		//启动
		st.start();
		//st.run(); 普通方法调用
		for(int i=0;i<20;i++){
			System.out.println("一边coding");
		}
	}
}

在这里插入图片描述

图片下载
  • WebDownloader
/**
 * 下载图片
 * 
 */
package com.jsp.thread;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.io.FileUtils;

/**
 * @author Jsp
 *
 */
public class WebDownloader {
	/**
	 * 下载
	 * @param url
	 * @param name
	 */
	public void download(String url,String name) {
		try{
			FileUtils.copyURLToFile(new URL(url),new File(name) );
		}catch(MalformedURLException e){
			e.printStackTrace();
			System.out.println("不合法的url");
		}catch(IOException e){
			e.printStackTrace();
			System.out.println("下载失败");
		}
	}
}

  • TDownloader
package com.jsp.thread;

public class TDownloader extends Thread {
	private String url; //远程路径
	private String name;//存储名字
	
	public TDownloader(String url, String name) {
		
		this.url = url;
		this.name = name;
	}
	
	@Override
	public void run(){
		WebDownloader wd = new WebDownloader();
		wd.download(url, name);
	}
	
	public static void main(String[] args) {
		TDownloader td1 = new TDownloader("http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg","1.jpg");
		TDownloader td2 = new TDownloader("http://pic32.nipic.com/20130902/13395269_103525238102_2.jpg","2.jpg");
		TDownloader td3 = new TDownloader("http://pic41.nipic.com/20140508/18609517_112216473140_2.jpg","3.jpg");
		
		//启动三个线程
		td1.start();
		td2.start();
		td3.start();
	}
}
2,实现Runnable接口

另一种方法来创建一个线程是声明实现类Runnable接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。

class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }

然后,以下代码将创建一个线程并启动它运行:

PrimeRun p = new PrimeRun(143);
     new Thread(p).start();

示例:

/**
 * 
 */
package com.jsp.thread;

/**
 * 创建线程方式2
 * 1,创建:实现Runnable+重写run
 * 2,启动:创建实现类对象+Thread对象+start
 * 推荐:避免单继承的局限性,优先使用接口
 * 方便资源共享
 * @author Jsp
 *
 */
public class StartRun implements Runnable {
	/**
	 * 线程入口
	 */
	@Override
	public void run() {
		for(int i=0;i<20;i++){
			System.out.println("一边听歌");
		}
	}
	
	public static void main(String[] args) {
		/*//创建实现类对象
		StartRun st = new StartRun();
		//创建代理类对象
		Thread t = new Thread(st);
		//启动
		t.start();
		//st.run(); 普通方法调用
		*/		
		new Thread(new StartRun()).start();
		for(int i=0;i<20;i++){
			System.out.println("一边coding");
		}
	}
}

二:

package com.jsp.thread;
/**
 * 共享资源,并发
 * @author Jsp
 *
 */
public class Web12306 implements Runnable{
	//票数
	private int ticketNums = 99;
		
	@Override
	public void run() {
		while (true) {
			if(ticketNums<0){
				break;
			}
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"---------"+ticketNums--);
		}
		
	}
	public static void main(String[] args) {
		//一份资源
		Web12306 web = new Web12306();
		System.out.println(Thread.currentThread().getName());
		//多个代理
		new Thread(web,"malong").start();
		new Thread(web,"lagong").start();
		new Thread(web,"hahong").start();
	}

}

模拟龟兔赛跑

package com.jsp.thread;
/**
 * 
 * @author Jsp
 *
 */
public class Racer implements Runnable {
	private static String winner;//胜利者
	
	@Override
	public void run() {
		for (int steps = 1; steps <= 100; steps++) {
			//模拟兔子每10步休息100ms
			if (Thread.currentThread().getName().equals("rabbit")&&steps%10==0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+"-----"+steps);
			//比赛是否结束
			boolean flag = gameOver(steps);
			if (flag) {
				break;
			}
		}
		
	}
	private boolean gameOver(int steps){
		if (winner!=null) {
			return true;
		}else{
			if (steps==100) {
				winner = Thread.currentThread().getName();
				System.out.println("winner=="+winner);
				return true;
			}
		}
		return false;
	}
	public static void main(String[] args) {
		Racer racer = new Racer();
		new Thread(racer,"tortoise").start();
		new Thread(racer,"rabbit").start();
	}
}

3,实现Callable接口

代码

package com.jsp.thread;
 
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
 * 了解创建线程的方式三: 
 * @author Jsp
 */
public class CDownloader implements Callable<Boolean>{
    private String url; //远程路径
    private String name;  //存储名字
     
    public CDownloader(String url, String name) {
        this.url = url; 
        this.name = name;
    }
 
    @Override
    public Boolean call() throws Exception {
        WebDownloader wd =new WebDownloader();
        wd.download(url, name);     
        System.out.println(name);
        return true;
    }
     
public static void main(String[] args) throws InterruptedException, ExecutionException {
	CDownloader cd1 =new CDownloader("http://upload.news.cecb2b.com/2014/0511/1399775432250.jpg","phone.jpg");
    CDownloader cd2 =new CDownloader("http://p1.pstatp.com/large/403c00037462ae2eee13","spl.jpg");
    CDownloader cd3 =new CDownloader("http://5b0988e595225.cdn.sohucs.com/images/20170830/d8b57e0dce0d4fa29bd5ef014be663d5.jpeg","success.jpg");
         
    //创建执行服务: 
    ExecutorService  ser=Executors.newFixedThreadPool(3);
    //提交执行: 
    Future<Boolean> result1 =ser.submit(cd1) ;
    Future<Boolean> result2 =ser.submit(cd2) ;
    Future<Boolean> result3 =ser.submit(cd3) ;
    //获取结果:  
    boolean r1 =result1.get();
    boolean r2 =result2.get();
    boolean r3 =result3.get();
    System.out.println(r3);
    //关闭服务:  
    ser.shutdownNow();
 
   }
}

更多学习链接:https://www.runoob.com/java/java-files-io.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值