1.创建线程一:继承Thread 类
-
子类继承Thread 类具备多线程能力
-
启动线程:子类对象,.start()
-
不建议使用:避免OOP单继承局限性
创建线程方式一:继承Thread类,重写run()方法,调用start开启线程 注意:线程开启不一定立即执行,由CPU调度执行
public class TestThread extends Thread {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码~~~");
}
}
public static void main(String[] args) {
//main线程,主线程
//创建一个线程对象
TestThread testThread = new TestThread();
//调用start()方法开启线程
testThread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在学习多线程");
}
}
}
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习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://tse4-mm.cn.bing.net/th/id/OIP-C.nip7Mn96BGdUdjG6zOWRnAHaLH?w=186&h=279&c=7&r=0&o=5&dpr=1.05&pid=1.7","1.jpg");
TestThread2 t2 = new TestThread2("https://tse2-mm.cn.bing.net/th/id/OIP-C.Jtsv_OsQj2kn-3aKZrWr2wHaJp?pid=ImgDet&rs=1","2.jpg");
TestThread2 t3 = new TestThread2("https://img.zcool.cn/community/015ea15d5cbdf6a8012187f458d58f.jpg@1280w_1l_2o_100sh.jpg","3.jpg");
t1.start();
t2.start();
t3.start();
}
}
//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
二、创建线程方式二:实现Runable接口
-
定义MyRunnable类实现Runable接口
-
重写run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
-
避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用
实现runnable接口,重写run()方法,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run方法中的输出语句");
}
}
public static void main(String[] args) {
//创建Runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();
//创建线程对象,通过线程对象来开启我们的线程,代理
Thread thread = new Thread(testThread3);
thread.start();
//new Thread(testThread3).start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程的输出语句");
}
}
}
初识并发问题:多个线程同时操作同一个对象
发现问题:多个线程操作同一个资源的情况下,线程不安全
龟兔赛跑
package thread;
//模拟龟兔赛跑
public class Race implements Runnable{
private String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉
if(Thread.currentThread().getName().equals("兔子")&&i%10 ==0){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = jieguo(i);
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
}
}
//判断是否完成比赛
private boolean jieguo (int steps){
if (winner !=null){ //已经存在胜利者
return true;
}else if(steps >= 100){
winner = Thread.currentThread().getName();
System.out.println("winner is "+winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
//实现runnable接口的对象,丢到Thread里面
new Thread(race,"乌龟").start();
new Thread(race,"兔子").start();
}
}
静态代理模式总结:
-
真实对象和代理对象都要实现同一个接口
-
代理对象代理真实角色
lambda表达式
函数式接口 Functional Interface:只包含唯一一个抽象方法。对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。
使用Lambda 表达式的前提是函数式接口。
Lambda的推导
package thread;
/*推导lambda表达式*/
public class TestLambda01 {
//3、静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("静态内部类的输出");
}
}
public static void main(String[] args) {
//接口去new一个他的实现类
ILike like = new Like(); // 实现类
like.lambda();
like =new Like2(); //静态内部类
like.lambda();
//4、局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("局部内部类的输出");
}
}
like = new Like3();
like.lambda();
//5、匿名内部类:没有类的名称,必须借助接口或者父类
like = new ILike() {
@Override
public void lambda() {
System.out.println("匿名内部类的输出");
}
};
like.lambda();
//6、用Lambda简化
like =()->{
System.out.println("lambda的输出");
};
like.lambda();
}
}
//1、定义一个函数式接口
interface ILike{
void lambda();
}
//2、实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("实现类的输出");
}
}
Lambda表达式的简化
public class TestLambda02 {
public static void main(String[] args) {
Study1 stu = (int a)->{
System.out.println("输出"+a);
};
stu.add(5);
//简化1、去掉参数类型(多个参数也可以,必须加括号)
stu = (a)->{
System.out.println("输出"+a);
};
stu.add(8);
//简化2、简化括号
stu = a->{
System.out.println("输出"+a);
};
stu.add(9);
//简化3、去掉花括号(在只有一行的情况下)
stu = a-> System.out.println("666"+a);
stu.add(888);
}
}
interface Study1{
void add(int a);
}
线程状态
-
创建状态:Thread t = new Thread()线程对象一旦创建就进入到了新生状态
-
就绪状态:当调用start()方法,线程立即进入就绪状态,但不意味着立即调度执行
-
运行状态:进入运行状态,线程才真正执行线程体的代码块
-
阻塞状态:当调用sleep,wait 或同步锁定时,线程进入阻塞状态,就是代码不往下执行,阻塞时间解除后,重新进入就绪状态,等待CPU调度执行。
-
死亡状态:线程中断或者结束,一旦进入死亡状态,就不能再次启动。
停止线程
不推荐使用JDK提供的stop()、destroy()方法【已废弃】;推荐线程自己停止下来
建议使用一个标志位进行终止变量当flag=false,则终止线程运行。
//测试停止线程
// 1、建议线程正常停止---->利用次数,不建议死循环。
// 2、建议使用标志位--->设置一个标志位
public class TestStop implements Runnable{
// 1、设置一个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("线程运行"+ i++);
}
}
// 2、设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main---"+i);
if (i==800){
testStop.stop();
System.out.println("线程该停止了");
}
}
}
}
线程休眠
-
sleep需要抛出异常
-
sleep时间达到后线程进入就绪状态
-
sleep可以模拟网络延时、倒计时等。
-
每一个对象都有一个锁,sleep不会释放锁;※※
模拟网络延时:放大问题的发生性
package thread;
//模拟网络延时
public class TestSleep implements Runnable{
//火车票数
private int ticketNums = 10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
//模拟延时
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
}
}
public static void main(String[] args) {
TestSleep ticket = new TestSleep();
new Thread(ticket,"小明星").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛").start();
}
}
模拟倒计时
package thread; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.SimpleFormatter; public class TestSleep2 { public static void main(String[] args) { // 打印当前系统时间 Date time = new Date(System.currentTimeMillis()); while (true){ try { Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(time)); time = new Date(System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } //模拟倒计时,直接在main方法中调用turnDown()方法 public static void turnDown() throws InterruptedException { int num = 10; while (true){ Thread.sleep(1000); System.out.println(num--); if(num<=0){ break; } } } }
线程礼让:礼让不一定成功
//测试礼让线程,礼让不一定成功
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束");
}
}
Join
想象为线程插队
public class TestJoin implements Runnable{
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 500; i++) {
if (i==100){
thread.join();
}
System.out.println("main"+i);
}
}
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("线程vip"+i);
}
}
}