Java 多线程
一、进程 / 线程
进程:启动一个application,就调度了一个进程,CPU分配内存
线程:进程中的一部分,相当于进程中的一条路径,多线程,就是多条路径。线程资源共享,CPU不额外分配内存
二、Java实现多线程
1、 继承Thread,重写run方法
/**
1. 创建多线程,继承Thread,重写run()方法
2. 使用线程:创建子对象 + 对象.start(),线程启动
*/
public class Rabbit extends Thread {
@Override
public void run() {
//线程体
for (int i = 0; i <= 20; i++) {
System.out.println("Rabbit has run " + i + " steps.");
}
}
}
class Turtle extends Thread {
@Override
public void run() {
for (int i = 0; i <= 5; i++) {
System.out.println("Turtle has run " + i + " steps.");
}
}
}
public class Main {
public static void main(String[] args) {
Rabbit rabbit = new Rabbit();
Turtle turtle = new Turtle();
rabbit.start(); //调用start方法,系统会自动调用run方法,不要直接调用run方法
turtle.start(); //这样就有两条路径了
}
}
2、实现Runnable接口 + 重写run()方法
/**
推荐使用Runnable接口
1、避免Thread单继承的问题
2、方便资源共享
*/
public class Traveler implements Runnable {
private int numberoftickets = 20;
@Override
public void run() {
while (true){
if (numberoftickets<0){
break;
}
System.out.println(Thread.currentThread().getName() + " 抢到了 " + numberoftickets--);
}
}
public static void main(String[] args) {
//真实角色 - traveller
Traveler traveler = new Traveler();
//代理 - Thread
Thread t1 = new Thread(traveler, "黄牛1号");
Thread t2 = new Thread(traveler, "黄牛2号");
Thread t3 = new Thread(traveler, "黄牛3号");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
3、实现callable接口 + 重写call
这个方法实现起来较复杂
优点:
- 可以抛出异常
- 可以返回值
三、线程的状态和停止线程
public class TestDemo { public static void main(String[] args) { Study s = new Study(); new Thread(s).start(); //外部干涉 for (int i = 0; i<20; i++){ if (i==10){ s.stop(); } System.out.println("I'm taking break..." + i); } } } class Study implements Runnable{ //1、线程类中,定义线程体使用的标示 private boolean flag = true; @Override public void run() { //2、线程体使用该标示 while (flag){ System.out.println("I'm studying..." ); } } //3、对外提供方法改变标示 public void stop(){ flag = false; } }
//尽量不要调用Thread.stop()方法,而是自己写一个外部干涉
//或者让线程体运行完自然停止
四、线程阻塞
1、join,合并线程,被合并的线程需要等去合并的线程执行完,所以被合并的线程阻塞
2、yeild,static,暂停当前正在进行的线程,执行其他线程
3、sleep,休眠,暂停当前线程,不会释放锁。
- 常用于与时间相关:ex: 倒计时
- 网络延时
/** * 倒计时10秒 */ public class SleepDemo { public static void main(String[] args) throws InterruptedException { Date endTime = new Date(System.currentTimeMillis()+10*1000); long end = endTime.getTime(); while (true){ //输出 System.out.println(new SimpleDateFormat("mm:ss").format(endTime)); //构建下一秒的时间 endTime = new Date(endTime.getTime() - 1000); //等待1秒 Thread.sleep(1000); //10秒以内继续输出,否则break if (end - 10000 > endTime.getTime()){ break; } } } }
五、线程的基本信息
- Thread.currentThread()
- setName()
- getName()
- isAlive, 返回true/false
优先级:
- MAX_PRIORITY 10
- NORMALLY_PRIORITY 5
- MIN_PRIORITY 1
Thread.setPriority //设置优先级 (优先级不代表先后顺序,而代表概率。)
六、线程同步与锁定
同步:也称为并发,多个线程访问同一份资源,要确保资源安全
关键字:synchronized
/**
* 懒汉式
*/
public class TestDemo {
private static TestDemo instance; //1、声明一个私有化的静态属性
private TestDemo(){ //2、私有化构造器
}
public static TestDemo getInstance(){ //3、声明公共的访问方法,来访问此静态属性
if (instance==null){ //第一层判断,提供效率
synchronized (TestDemo.class){
if (instance==null){ //第二层判断,确保线程安全
instance = new TestDemo();
}
}
}
return instance;
}
}
/** * 饿汉式 */ public class TestDemo { private static class TestDemoInner { private static TestDemo instance = new TestDemo(); //1、直接内部先私有化一个对象(饿嘛) }
//把这个私有属性放进一个内部类,主要是为了效率,否则TestDemo类一加载,这个私有属性就会被初始化, //而放在内部类中,只有加载get方法,才会初始化
private TestDemo(){ //2、私有化构造器 } public static TestDemo getInstance(){ //3、声明公共的访问方法,返回的是内部类.instance return TestDemoInner.instance; } }
与sychronized可以一起使用的两个方法:wait() , notify() / notifyAll()
生产者消费者模式 (信号灯法):其中一个线程生产,另一个wait,生产完毕之后notify,另一个开始消费,有效避免死锁。
线程任务调度:
Timer()
Timer.schedule(TimerTask task, Date time)
Timer.schedule(TimerTask task, Date firstTime, long period)
浙公网安备 33010602011771号