线程简介
- 方法间的调用:普通方法调用,从哪里来到哪里去,闭合的一条路径
- 多线程使用:开辟了多条路径
核心概念:
- 线程就是独立的执行路径;
- 在程序运行时,即使没有自己创建线程,后台也会存在多个线程,如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();
}
}