- 创建线程
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 推荐使用Runnable接口
- 可以使用多个代理(静态代理模式)
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) {
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,"码畜").start();
new Thread(web,"码农").start();
new Thread(web,"码蟥").start();;
}
}
- Lambda表达式
- 函数化接口
- lambda推导必须存在类型
- 线程状态


- 进入就绪状态的原因
1) start()
2) 阻塞时间解除
3) Thread.yield(),当前线程主动让出CPU(从运行态回到就绪态)——礼让。
4) jvm从本线程切换到其他线程
- 进入阻塞状态的原因
1) Thread.sleep(),当前线程不交出cpu等待。
2) wait(),交出cpu等待。
3) 对象方法join()。等待插队进来的线程执行完——写在哪个线程中,则哪个线程被阻塞。而调用join()的对象是插队的线程。
4) 等待IO操作
注意wait()的使用:
- 必须在synchronized块中调用
- 调用wait()后线程会释放持有的对象锁并进入阻塞状态
- join()示意

- join()的代码
public class Thread_b {
public static void main(String[] args) throws InterruptedException {
Thread thread_a = new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println("thread_a要超车了..." + i);
}
});
thread_a.start();
for (int i = 0; i < 100; i++) {
if (i == 20) {
System.out.println("thread_a插队!!");
thread_a.join(); // 插队,main主线程(thread_b)被阻塞
}
System.out.println("thread_b正常行驶..." + i);
}
}
}
join(milliseconds),参数为超时的时间,超过时间还没完,则不等了,让CPU自行调度。
join()可以实现线程的同步。
- 线程的状态
Thread t=new Thread(()->{
System.out.println("...");
});
State state=t.getState();
...
//指定线程状态
Thread.State.TERMINATED
...
//获取活动线程数
Thread.activeCount()
- 守护线程
默认都为用户线程。
Thread t=new Thread(god);
t.setDaemon(true);
- synchronized和同步对象
- synchronized方法的同步对象是this
- synchronized块可以自定义同步对象(减小锁定的粒度)
synchronized (object){ //这里的同步对象一般是要修改的对象
...
}
同步对象必须是不变的对象——对象不变(引用的地址不变),只是属性变。对于变化的对象(引用不同的对象地址)是锁不住的。
需要尽可能锁定合理的范围(不是指代码,而是指数据的完整性)。
- double checking检验数据的临界值
...
public void test5() {
if(ticketNums<=0) {//考虑的是没有票的情况
flag = false;
return ;
}
synchronized(this) {
if(ticketNums<=0) {//考虑最后的1张票
flag = false;
return ;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
}
}
...
-
死锁
不要在一个同步块中同时持有多个对象的锁。 -
生产者消费者模型——线程同步(依序执行)
- 容器队列作缓冲区——管程法
/**
* 协作模型:生产者消费者实现方式一:管程法 借助缓冲区
*
* @author beeworkshop
*
*/
public class CoTest01 {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread {
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
public void run() {
// 生产
for (int i = 0; i < 100; i++) {
System.out.println("生产-->" + i + "个馒头");
container.push(new Steamedbun(i));
}
}
}
//消费者
class Consumer extends Thread {
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
public void run() {
// 消费
for (int i = 0; i < 100; i++) {
System.out.println("消费-->" + container.pop().id + "个馒头");
}
}
}
//缓冲区
class SynContainer {
Steamedbun[] buns = new Steamedbun[10]; // 存储容器
int count = 0; // 计数器
// 存储 生产
public synchronized void push(Steamedbun bun) {
// 何时能生产 容器存在空间
// 不能生产 只有等待
if (count == buns.length) {
try {
this.wait(); // 线程阻塞 消费者通知生产解除
} catch (InterruptedException e) {
}
}
// 存在空间 可以生产
buns[count] = bun;
count++;
// 存在数据了,可以通知消费了
this.notifyAll();
}
// 获取 消费
public synchronized Steamedbun pop() {
// 何时消费 容器中是否存在数据
// 没有数据 只有等待
if (count == 0) {
try {
this.wait(); // 线程阻塞 生产者通知消费解除
} catch (InterruptedException e) {
}
}
// 存在数据可以消费
count--;
Steamedbun bun = buns[count];
this.notifyAll(); // 存在空间了,可以唤醒对方生产了
return bun;
}
}
//馒头
class Steamedbun {
int id;
public Steamedbun(int id) {
this.id = id;
}
}
注意:wait()和notifyAll()方法都是Object类提供的。notifyAll()的使用需要:
- 必须在synchronized块中调用
- 唤醒等待队列中的所有线程(notify()唤醒一个)
- 信号灯
/**
* 协作模型:生产者消费者实现方式二:信号灯法 借助标志位
*
* @author beeworkshop
*
*/
public class CoTest02 {
public static void main(String[] args) {
Tv tv = new Tv();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者 演员
class Player extends Thread {
Tv tv;
public Player(Tv tv) {
this.tv = tv;
}
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
this.tv.play("奇葩说");
} else {
this.tv.play("太污了,喝瓶立白洗洗嘴");
}
}
}
}
//消费者 观众
class Watcher extends Thread {
Tv tv;
public Watcher(Tv tv) {
this.tv = tv;
}
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//同一个资源 电视
class Tv {
String voice;
// 信号灯
// T 表示演员表演 观众等待
// F 表示观众观看 演员等待
boolean flag = true;
// 表演
public synchronized void play(String voice) {
// 演员等待
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 表演
System.out.println("表演了:" + voice);
this.voice = voice;
// 唤醒
this.notifyAll();
// 切换标志
this.flag = !this.flag;
}
// 观看
public synchronized void watch() {
// 观众等待
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 观看
System.out.println("听到了:" + voice);
// 唤醒
this.notifyAll();
// 切换标志
this.flag = !this.flag;
}
}
本文详细解析了Java线程的创建与使用,包括继承Thread类、实现Runnable接口、线程状态转换、同步与死锁避免、生产者消费者模型的两种实现方法。探讨了线程同步机制、线程间通信及如何利用容器队列和信号灯法解决线程间的协作问题。
2590

被折叠的 条评论
为什么被折叠?



