线程的生命周期
Thread类
如果不给Thread对象传name参数,那么new出来的线程对象默认叫Thread-0,依次增加。如果有被自定义name的线程对象,那么下一个无指定name的对象序号不受影响,会叫Thread-01。
常见实现多线程的方法
- 继承Thread类
1.基础
public class Thread01 extends Thread {
@Override
public void run() {
for (int i=0;i<5;i++)
{
System.out.println("我在看手机");
}
}
public static void main(String[] args) {
//创建Thread类子类对象
Thread01 thread01=new Thread01();
//调用start()方法,启用多线程,不保证立即运行
thread01.start();
//直接调用run()方法只是一个普通方法,start()才是开启多线程
//thread01.run();
for (int i=0;i<5;i++)//此时我在看手机和我在玩游戏各自进行。
{
System.out.println("我在玩游戏");
}
}
}
输出.png
- 多线程图片下载
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class PicDownloader {
public void download(String url,String name)
{
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
System.out.println("下载失败");
}
}
}
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() {
PicDownloader picDownloader=new PicDownloader();
picDownloader.download(url,name);
System.out.println(name);
}
public static void main(String[] args) {
TDownLoader tDownLoader1=new TDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682114283&di=434cb570d405250902076c4245dee68f&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D2247852322%2C986532796%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853","house.jpg");
TDownLoader tDownLoader2=new TDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682168382&di=a396192287a6522d2d999b0085dedba8&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D1484500186%2C1503043093%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853","lion.jpg");
TDownLoader tDownLoader3=new TDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682255390&di=283617ba468afa5d91d196b6ebc075e3&imgtype=0&src=http%3A%2F%2Ft9.baidu.com%2Fit%2Fu%3D583874135%2C70653437%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D3607%26h%3D2408","sheep.jpg");
//启动多线程
tDownLoader1.start();
tDownLoader2.start();
tDownLoader3.start();
//三个线程各自运行,并不一定会按1,2,3的顺序进行
}
}
输出.png
- 实现Runnable接口
public class Runnable01 implements Runnable {
@Override
public void run() {
for (int i=0;i<5;i++)
{
System.out.println("我在看手机");
}
}
public static void main(String[] args) {
//要调用start方法必须实例化Thread对象
Runnable01 r=new Runnable01();
new Thread(r).start();
for (int i=0;i<5;i++)
{
System.out.println("我在玩游戏");
}
}
}
输出.png
优先使用接口,因为java为多接口,单继承
- 模拟抢票测试多个线程之间的数据共享问题
public class My12306 implements Runnable {
private Integer tickets;
public My12306(Integer tickets) {
this.tickets = tickets;
}
public My12306()
{
}
@Override
public void run() {
//System.out.println("1111");
while (tickets>0)
{
System.out.println(Thread.currentThread().getName()+" --> Ticket"+tickets--);
}
System.out.println("没有票了哦");
}
public static void main(String[] args) {
My12306 t=new My12306(7);
new Thread(t,"小明").start();
new Thread(t,"小黄").start();
new Thread(t,"小红").start();
}
}
输出.png
如果把while循环里的内容更换成
while (tickets>0)
{
System.out.println(Thread.currentThread().getName()+" --> Ticket"+tickets);
tickets--;
}
就会出现
结果.png
由于多个线程共享tickets数据,所以出现了三个ticket7,可能涉及到了线程安全的问题。
- 模拟赛跑
public class Race implements Runnable{
private String winner;
@Override
public void run() {
for (int steps=1;steps<=100;steps++)
{
System.out.println(Thread.currentThread().getName()+"-->"+steps);
if (GameOver(steps))
{
break;
}
}
}
public boolean GameOver(int steps)
{
if (winner!=null)
{
return true;
}
else
{
if (steps==100)
{ winner=Thread.currentThread().getName();
System.out.println("Winner is "+winner);
return true;
}
else
{
return false;
}
}
}
public static void main(String[] args) {
Race race=new Race();
new Thread(race,"小明").start();
new Thread(race,"小红").start();
new Thread(race,"小光").start();
}
}
结果
-
第三个实现多线程的方法 实现callable方法-简单实现
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 {
PicDownloader picDownloader=new PicDownloader();
picDownloader.download(url,name);
System.out.println(name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CDownLoader cDownLoader1=new CDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682114283&di=434cb570d405250902076c4245dee68f&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D2247852322%2C986532796%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853","house.jpg");
CDownLoader cDownLoader2=new CDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682168382&di=a396192287a6522d2d999b0085dedba8&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D1484500186%2C1503043093%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853","lion.jpg");
CDownLoader cDownLoader3=new CDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682255390&di=283617ba468afa5d91d196b6ebc075e3&imgtype=0&src=http%3A%2F%2Ft9.baidu.com%2Fit%2Fu%3D583874135%2C70653437%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D3607%26h%3D2408","sheep.jpg");
//创建执行服务
ExecutorService service= Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> result1=service.submit(cDownLoader1);
Future<Boolean> result2=service.submit(cDownLoader2);
Future<Boolean> result3=service.submit(cDownLoader3);
//获取结果
boolean r1=result1.get();
boolean r2=result2.get();
boolean r3=result3.get();
//关闭服务
service.shutdownNow();
}
}
守护线程
线程分为两类 :守护线程(后台线程)和用户线程(前台线程),上述都是创建用户线程的方法。
守护线程为用户线程服务,用户线程结束之后守护线程也结束。
代码示例
public class Guardian {
public static void main(String[] args) {
Human human=new Human();
Universe universe=new Universe();
Thread thread1=new Thread(human);
Thread thread2=new Thread(universe);
thread1.start();
thread2.start();
}
}
class Human implements Runnable
{
@Override
public void run() {
for (int i=0;i<100;i++)
{
System.out.println("第"+i+"岁");
}
System.out.println("ByeBye");
}
}
class Universe implements Runnable
{
@Override
public void run() {
while (true)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我永远存在");
}
}
}
此时Universe的进程并不是守护进程,所以当Human进程结束之后Universe进程会一直进行,所以程序不会结束。
结果的一部分
使用
thread2.setDaemon(true);
将Universe转换为守护进程之后。
结果
结果
当Human进程结束之后,程序自动结束,Universe进程也结束。