多线程
2021-03-12 22:33
标签:过多 进程 主题 静态代理 运行 struct 打印 ide write 程序-----(运行)------>进程---------->线程 线程是独立的执行路径 程序运行时,即使自己没有创建线程,后台也会有多个线程---主线程,gc线程 一个进程中,如果开辟多个线程,线程的调度由调度器安排调度,而调度去与系统相关,人为无法干预 对同一份资源,操作时,会存在资源抢夺的问题,需要加入并发控制 线程会带来额外开销,如CPU调度时间,并发控制开销 每个线程只在自己的工作内存交互,互不干预 自定义线程类继承Tread类 重写run()方法,编写线程执行体 创建线程对象,调用start()方法其拆散多线程 线程开启不一定立即执行,由CPU调度执行,人为无法干预 testThread1.start()开启多线程后,线程时交替执行的 而testThread1.run()是调用TestThread1类中的run()方法,顺序执行,因此run执行完后才执行main 用多线程下载架包 将commons-io 2.6拷仅idea的lib目录 右键add as library 下载的顺序不是按t1,t2,t3,开启多线程后而是由系统自动调度分配 定义MYRunnable类实现Runable接口 实现run()方法,编写线程执行体 创建线程对象,调用start()方法启动线程 线程不安全 实现Callable接口,需要返回值类型 重现call方法,需要抛出异常 创建目标对象 创建执行服务 提交执行 获取结果 关闭服务 一个接口只有唯一的一个抽象方法 由于这个接口中只有这个方法,因此相比匿名内部类,可以实现除了类名,连方法名都可以 省略 推导lamda表达式(逐步简化) 关于内部类 示例2 如果方法重载了还满足lambda只有一个方法的要求吗? 不行 真实对象和代理对象都要实现同一个接口 代理对象要代理真实角色 不改变原有代码,去实现新的功能 代理对象可以做很多真实对象做不了的事 真实对象专注做自己的事 与多线程创建类比 真实对象和代理对象都要实现同一个接口 Thread 实现 Runnable接口 lambda表达式为一个匿名的实现类也是实现 Runnable接口 代理对象要代理真实角色 Thread真实代理lambda表达式的匿名类 ? 模拟网络延迟——放大问题的发生行 倒计时 系统时间 合并线程,待此线程执行完后,再执行其他线程,其他线程阻塞 可以想象成插队 开始时,两个线程交替进行, 但到主线程i=400开始,子线程的优先级变高,主线程开始等待,子线程执行完后才开始执行主线程 getState() 新生 new 就绪 运行 Runnable 死亡 Dead getPriority() setPriority(int x) 性能倒置 多跑几次,输出结果也可能是这样,因为设置优先级并不代表一定优先执行,而是优先执行的概率大,(和yield()一样,重新调度线程)具体还是看CPU调度,人为无法干预 线程分为用户线程和守护线程 虚拟机必须确保用户线程执行完毕 虚拟机不用等待守护线程执行完毕 线程2为while死循环,但线程1执行完,程序依旧结束了,因为虚拟机不用等待守护线程执行完毕 多个线程访问同一个对象的问题——并发问题 解决线程的安全性 锁提高安全性的同时,也会降低性能 买票案例 这个sleep放buy方法里,会导致第一个线程在run里不断调用buy,直接买完所有的票 放buy外面,sleep阻塞,下一个线程就有机会买到了 用sleep方法,放大问题的发生性 可以看见,黄牛,小红都抢到了第10张票——即表明线程不安全 银行取钱 取钱前,小明和妻子等待了1秒,同时操作1个对象,因此都能取出钱 list案例 99973而不是预想的100000,等待了100ms,同时添加1个对象,数据覆盖,因此线程也是不安全的 对属性的保护——private,写set/get方法 实现原理:队列与锁 但也有弊端 不是对象所有内容都需要同步的 修改部分需要同步,而只读部分不需要同步,浪费资源 买票案例 银行案例 list案例 JUC transient——临时的 volatile——不可被序列化 小明先拿到口红,小红先拿到镜子 但都卡住了,拿不到另一样东西 小明锁了口红时,要拿镜子这个资源,但镜子被小红锁了,要等待小红执行完解锁 小红锁了镜子时,要拿口红这个资源,但口红被小明锁了,要等待小明执行完解锁,这样就死锁了 ReentrantLock类——可重入锁 这是lock锁this这个对象,但如果要像Synchronized(特定对象),又该如何用lock实现? 生产者消费者问题 输出日志 关于wait 多线程 标签:过多 进程 主题 静态代理 运行 struct 打印 ide write 原文地址:https://www.cnblogs.com/Sheltonz/p/14070779.html多线程
一、线程简介
1. Process与Thread
二、线程的实现(重点)
1. 线程的创建(三种方式)
① Thread类
//继承Thread类
public class TestThread1 extends Thread {
//run方法——TestTread1线程
@Override
public void run() {
for (int i = 0; i
public class TestThread1 extends Thread {
//run方法——TestTread1线程
@Override
public void run() {
for (int i = 0; i
下载图片
//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{
private String url;//网络图片地址
private String name;//保存的文件名
public TestThread2(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载文件名为:"+name);
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://04imgmini.eastday.com/mobile/20201124/20201124125422_b97573c574f48a6af5f5c5c9a9beea1b_1.jpeg","aaa1.jpg");
TestThread2 t2 = new TestThread2("https://04imgmini.eastday.com/mobile/20201124/20201124125422_b97573c574f48a6af5f5c5c9a9beea1b_2.jpeg","aaa2.jpg");
TestThread2 t3 = new TestThread2("https://04imgmini.eastday.com/mobile/20201124/20201124125422_b97573c574f48a6af5f5c5c9a9beea1b_3.jpeg","aaa3.jpg");
t1.start();
t2.start();
t3.start();
}
}
② Runnable接口
public class TestThread3 implements Runnable{
//重现run方法
@Override
public void run() {
for (int i = 0; i
thread类和runnable接口的区别
多个线程同时操作同一个对象
public class TestThread4 implements Runnable{
private int ticketNum = 10;
@Override
public void run() {
while (true) {
if (ticketNum
龟兔赛跑
//龟兔赛跑
public class TestThread5 implements Runnable {
private static String winner;//胜利者
private final int LENGTH = 1000;//赛道长度
//设置赛道
@Override
public void run() {
for (int i = 0; i =LENGTH){
winner = Thread.currentThread().getName();
System.out.println(winner+"赢得了比赛");
return true;
}
//无胜利者,返回false
return false;
}
//主线程
public static void main(String[] args) {
TestThread5 race = new TestThread5();
new Thread(race, "兔子").start();
new Thread(race, "乌龟").start();
}
}
③ Callable接口
public class TestCallable implements Callable{
private String url;
private String name;
public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}
//重写call方法
@Override
public Boolean call() throws Exception {
this.download(url,name);
System.out.println("下载文件名为:"+name);
return true;
}
//下载方法
public void download(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://04imgmini.eastday.com/mobile/20201124/20201124125422_b97573c574f48a6af5f5c5c9a9beea1b_1.jpeg","bbb1.jpg");
TestCallable t2 = new TestCallable("https://04imgmini.eastday.com/mobile/20201124/20201124125422_b97573c574f48a6af5f5c5c9a9beea1b_2.jpeg","bbb2.jpg");
TestCallable t3 = new TestCallable("https://04imgmini.eastday.com/mobile/20201124/20201124125422_b97573c574f48a6af5f5c5c9a9beea1b_3.jpeg","bbb3.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future
Callable的好处
2. Lambda表达式
函数式接口
public interface Runnable{
public abstruct run();
}
public class TestLambda {
//成员内部类
class Like2 implements ILike {
@Override
public void show() {
System.out.println("接口实现方式——成员内部类");
}
}
//静态内部类
static class Like3 implements ILike {
@Override
public void show() {
System.out.println("接口实现方式——静态内部类");
}
}
public static void main(String[] args) {
//1.正常操作 —— 在外部写接口的实现类
new Like1().show();
//2. 成员内部类 —— 将实现类写到内部
TestLambda testLambda = new TestLambda();
testLambda.new Like2().show();
//3. 静态内部类
new Like3().show();
//4. 局部内部类
class Like4 implements ILike {
@Override
public void show() {
System.out.println("接口实现方式——局部内部类");
}
}
new Like4().show();
//5. 匿名内部类
new ILike() {
@Override
public void show() {
System.out.println("接口实现方式——匿名内部类");
}
}.show();
//6. lambda简化
ILike like = ()->{
System.out.println("接口的实现方式——lambda表达式");
};
like.show();
}
}
//定义一个函数式接口
interface ILike {
void show();
}
//接口实现类
class Like1 implements ILike {
@Override
public void show() {
System.out.println("接口实现方式——写实现类(外部)");
}
}
public class TestLambda2 {
public static void main(String[] args) {
//匿名内部类
new Meals() {
@Override
public void show(String name, String food, int nums) {
System.out.println(name+"吃了"+nums+"份"+food);
}
}.show("早餐","面包",3);
//lambda表达式
Meals meals;
meals = (a,b,c)->{
System.out.println(a+"吃了"+c+"份"+b);
};
meals.show("早餐","面包",3);
//简化lambda表达式
meals = (a,b,c) -> System.out.println(a+"吃了"+c+"份"+b);
meals.show("早餐","面包",3);
}
}
interface Meals{
void show(String name, String food, int nums);
}
3. 静态代理模式
好处
public class StaticProxy {
public static void main(String[] args) {
People you = new People();
new WeddingCompany(you);
}
}
interface Merry{
void merry();
}
//你——真实对象
class People implements Merry{
@Override
public void merry() {
System.out.println("我终于结婚了,超开心TAT");
}
}
//婚庆公司——代理
class WeddingCompany implements Merry{
private Merry customer;
public WeddingCompany(Merry customer) {
this.customer = customer;
this.merry();
}
@Override
public void merry() {
berfore();
customer.merry();
after();
}
private void berfore() {
System.out.println("结婚前,布置婚礼");
}
private void after() {
System.out.println("结婚后,收取尾款");
}
}
/*
结婚前,布置婚礼
我终于结婚了,超开心TAT
结婚后,收取尾款
*/
//静态代理模式
new WeddingCompany(new People()).merry();
//线程创建也构成静态代理的要求
new Thread( ()->System.out.println("love")).start();
三、线程状态
1.线程的五种状态
2. 如何停止线程
public class TestStop implements Runnable{
boolean flag = true;
@Override
public void run() {
//子线程就两条语句,当while执行完,子线程就结束了,但while(true)为无限循环,也就是说子线程只有在flag变为false的时候才会停止
int i = 0;
while (flag) {
System.out.println("*****************子线程跑了"+i++);
}
}
//停止线程
void stop() {
flag = false;
System.out.println("**************子线程停止了");
}
public static void main(String[] args) {
TestStop tt = new TestStop();
new Thread(tt).start();
for (int i = 0; i
3. sleep()
public class TestThread4 implements Runnable{
private int ticketNum = 10;
@Override
public void run() {
while (true) {
if (ticketNum
//倒计时
public class TestSleep {
public static void main(String[] args) {
for (int i = 10; i > 0; i--) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
/*
10
9
8
...
1
*/
public class TestSleep2 {
public static void main(String[] args) {
//系统时间
Date date;
while (true) {
try {
date = new Date(System.currentTimeMillis());
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*
21:32:09
21:32:10
21:32:11
21:32:12
...
*/
4. yield()
public class TestYield {
public static void main(String[] args) {
new Thread(new ThreadYield(),"线程1").start();
new Thread(new ThreadYield(),"线程2").start();
}
}
class ThreadYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"运行中");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"已结束");
}
}
/*
线程1运行中
线程2运行中
线程2已结束
线程1已结束
*/
5. join()
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i
6. 线程的状态观测
public class TestState{
//观察子线程
public static void main(String[] args) {
//1.新生 2.就绪
Thread t1 = new Thread(()->{
try {
//4.阻塞
Thread.sleep(1000);
} catch (InterruptedExc eption e) {
e.printStackTrace();
}
//5.执行完这句话--->死亡
System.out.println("lsat sentence");
});
System.out.println(t1.getState());//观察状态
//3.启动
t1.start();
System.out.println(t1.getState());
//启动之后
while (t1.getState() != Thread.State.TERMINATED) {
//只要子线程不终止,在主线程中就不停检测子线程状态
try {
Thread.sleep(100);
System.out.println(t1.getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
7. 线程的优先级
public static void main(String[] args) {
//查看主线程优先级
System.out.println(Thread.currentThread().getName() + "优先级为" + Thread.currentThread().getPriority());
//创建线程
Thread t1 = new Thread(() -> System.out.println(Thread.currentThread().getName() + "优先级为" + Thread.currentThread().getPriority()));
Thread t2 = new Thread(() -> System.out.println(Thread.currentThread().getName() + "优先级为" + Thread.currentThread().getPriority()));
Thread t3 = new Thread(() -> System.out.println(Thread.currentThread().getName() + "优先级为" + Thread.currentThread().getPriority()));
Thread t4 = new Thread(() -> System.out.println(Thread.currentThread().getName() + "优先级为" + Thread.currentThread().getPriority()));
//设置优先级
t1.setPriority(7);
t2.setPriority(3);
t3.setPriority(Thread.MAX_PRIORITY);
t4.setPriority(1);
//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/*
main优先级为5
Thread-2优先级为10
Thread-0优先级为6
Thread-1优先级为3
Thread-3优先级为1
*/
/*
main优先级为5
Thread-0优先级为6
Thread-2优先级为10
Thread-1优先级为3
Thread-3优先级为1
*/
8. 守护线程
public class TestDeamon {
public static void main(String[] args) {
//线程1——输出五秒系统时间
new Thread(()->{
for (int i = 0; i {
while (true) {
System.out.println("==守护线程==");
}
});
t2.setDaemon(true);//线程默认false,true后变为守护线程
t2.start();
}
}
/*
==守护线程==
==守护线程==
15:46:49
==Goodbye==
==守护线程==
==守护线程==
==守护线程==
*/
四、线程同步(重点)
1. 线程同步机制
队列+锁
2. 为什么线程是不安全的
public class Ticket {
public static void main(String[] args) {
TicketSale station = new TicketSale();
new Thread(station, "小明").start();
new Thread(station, "小红").start();
new Thread(station, "黄牛").start();
}
}
class TicketSale implements Runnable{
private int ticketNums = 10;
private boolean flag = true;
@Override
public void run() {
//买票
while (flag) {
if (ticketNums
/*
黄牛抢到了第10张票
小明抢到了第9张票
小红抢到了第10张票
小红抢到了第8张票
小明抢到了第7张票
黄牛抢到了第6张票...*/
public class BankTest {
public static void main(String[] args) {
System.out.println("=======小明夫妻共有5000元存款=======");
Account account = new Account("存款", 5000);
new Thread(new Drawing(account, 100), "小明").start();
new Thread(new Drawing(account, 5000), "妻子").start();
}
}
//账户
class Account{
private String name;//账户名
private int money;//存款
public Account(String name,int money) {
this.name = name;
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
//银行取款
class Drawing implements Runnable {
Account account;//账户
int withdraw;//取现
int cash;//现金
public Drawing(Account account, int withdraw) {
this.account = account;
this.withdraw = withdraw;
}
@Override
public void run() {
//如果没有钱退出
if (account.getMoney() - withdraw
public class ListTest {
public static void main(String[] args) {
List list = new ArrayList();
for (int i = 0; i {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
//99973
3. 如何解决线程不安全
同步方法
同步块
private synchronized void buyTicket (){
System.out.println(Thread.currentThread().getName()+"抢到了第"+(ticketNums--)+"张票");
}
}
/*
小明抢到了第10张票
小红抢到了第9张票
黄牛抢到了第8张票
小红抢到了第7张票
小明抢到了第6张票
黄牛抢到了第5张票
小红抢到了第4张票
小明抢到了第3张票
黄牛抢到了第2张票
小红抢到了第1张票
*/
@Override
public void run() {
synchronized (account) {
//如果没有钱退出
if (account.getMoney() - withdraw
for (int i = 0; i {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
//100000
public class TestJUC {
public static void main(String[] args) {
//安全类型的集合
CopyOnWriteArrayList
死锁
public class DeadLock {
public static void main(String[] args) {
System.out.println("0=口红,1=镜子");
new MyMakeup("小明",0).start();
new MyMakeup("小红",1).start();
}
}
class Mirror {}
class Lipstick {}
class MyMakeup extends Thread {
//资源
static Mirror mirror = new Mirror();
static Lipstick lipstick = new Lipstick();
//人物、选择
String name;
int choice;
public MyMakeup(String name, int choice) {
super(name);
this.choice = choice;
}
@Override
public void run() {
//化妆
makeup();
}
//化妆,互相持有对方的锁——需要拿到对方的资源
private void makeup() {
name = Thread.currentThread().getName();
if (choice==0) {
//获得口红
synchronized (lipstick){
System.out.println(name+"拿到了口红");
//使用了1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获得镜子
synchronized (mirror) {
System.out.println(name+"拿到了镜子");
}
}
} else {
//获得镜子
synchronized (mirror) {
System.out.println(name+"拿到了镜子");
////使用了1秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获得口红
synchronized (lipstick) {
System.out.println(name+"拿到了口红");
}
}
}
}
}
/*
0=口红,1=镜子
小明拿到了口红
小红拿到了镜子
*/
private void makeup() {
name = Thread.currentThread().getName();
if (choice==0) {
//获得口红
synchronized (lipstick){
System.out.println(name+"拿到了口红");
//使用了1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"放回了口红");
}
//获得镜子
synchronized (mirror) {
System.out.println(name+"拿到了镜子");
}
} else {
//获得镜子
synchronized (mirror) {
System.out.println(name+"拿到了镜子");
////使用了1秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"放回了镜子");
}
//获得口红
synchronized (lipstick) {
System.out.println(name+"拿到了口红");
}
}
}
/*
0=口红,1=镜子
小红拿到了镜子
小明拿到了口红
小明放回了口红
小明拿到了镜子
小红放回了镜子
小红拿到了口红
*/
LOCK
public class TestLock {
public static void main(String[] args) {
Test test = new Test();
new Thread(test,"线程1").start();
new Thread(test,"线程2").start();
new Thread(test,"线程3").start();
}
}
class Test implements Runnable {
ReentrantLock lock = new ReentrantLock();//定义Lock锁
int num = 10;
@Override
public void run() {
while(true) {
try {
lock.lock();//加锁
if (num>0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" :"+num--);
} else {
break;
}
} finally {
lock.unlock();//解锁
}
}
}
}
五、线程通信问题
1. 管程法
public class Demo02 {
public static void main(String[] args) {
Buffer buffer = new Buffer();//缓冲区
new Productor(buffer).start(); //生产者
new Consumer(buffer).start(); //消费者
}
}
//产品
class Product {
int id;
public Product(int id) {
this.id = id;
}
}
//缓冲区
class Buffer {
//1. 定义一个容器——用于存放产品
Product[] container = new Product[10];//可以存10个
int count = 0;//产品个数
//2. 生产者把东西放到缓冲区的方法
synchronized int push (Product product) {
//如果容器满了,就通知生产者停止生产
if (count>= container.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果容器没满,生产者一直会生产
//将生产的产品存入缓冲区
container[count++] = product;
//通知消费者消费
this.notifyAll();
return count;
}
//3. 消费者把东西从到缓冲区取出的方法
synchronized Product pop() {
//如果容器空了,通知消费者等待
if (count =100) {
break;
}
}
}
}
//消费者
class Consumer extends Thread{
Buffer buffer;
public Consumer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
int count = 1;
while(true){
//这里buffer.pop()返回的是一个product对象所有能用.id
System.out.println(" "+buffer.pop().id+"号完成消费");
if (count>=100) {
return;
}
}
}
}
生产了1号产品
===投放1号===
===仓库共有1件产品==========
生产了2号产品
===投放2号===
===仓库共有2件产品==========
生产了3号产品
===投放3号===
===仓库共有3件产品==========
生产了4号产品
===投放4号===
===仓库共有4件产品==========
生产了5号产品
===投放5号===
===仓库共有5件产品==========
生产了6号产品
===投放6号===
===仓库共有6件产品==========
生产了7号产品
===投放7号===
===仓库共有7件产品==========
生产了8号产品
===投放8号===
===仓库共有8件产品==========
生产了9号产品
===投放9号===
===仓库共有9件产品==========
生产了10号产品
===投放10号===
===仓库共有10件产品==========
===取出10号===
10号完成消费
生产了11号产品
===投放11号===
===仓库共有10件产品==========
===取出11号===
11号完成消费
===取出12号===
12号完成消费
===取出9号===
9号完成消费
===取出8号===
8号完成消费
===取出7号===
7号完成消费
===取出6号===
6号完成消费
===取出5号===
5号完成消费
===取出4号===
4号完成消费
===取出3号===
3号完成消费
===取出2号===
2号完成消费
===取出1号===
1号完成消费
生产了12号产品
===投放12号===
===仓库共有10件产品==========
生产了13号产品
===投放13号===
===仓库共有1件产品==========
生产了14号产品
===投放14号===
===仓库共有2件产品==========
===取出14号===
14号完成消费
===取出13号===
13号完成消费
生产了15号产品
===投放15号===
===仓库共有1件产品==========
......
2. 信号灯法
//信号灯法
public class Demo03 {
public static void main(String[] args) {
TvShow tvShow = new TvShow();
new Actors(tvShow).start();
new Audiences(tvShow).start();
}
}
//演员——生产者
class Actors extends Thread {
TvShow tvShow;
public Actors(TvShow tvShow) {
super("演员");
this.tvShow = tvShow;
}
@Override
public void run() {
for (int i = 0; i
六、高级主题
1. 线程池
2.用Runnable实现线程池
public class executorService {
public static void main(String[] args) {
//开启线程池
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new Test());//execute(),提交runnable实现的线程
service.execute(new Test());
service.execute(new Test());
service.execute(new Test());
service.submit(new Test());//通用的提交线程方法
service.submit(new Test());
service.submit(new Test());
//关闭线程池
service.shutdown();
}
}
class Test implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"helloworld");
}
}
七、总结
1. 创建线程的三种方法
public class Demo01 {
public static void main(String[] args) {
//Thread
new Thread1().start();
//Runnable
new Thread(new Thread2()).start();
//Callable
FutureTask