第3 章 : 线程的同步与死锁
14 同步问题引出
Thread描述每一个线程对象
Runnable描述多个线程操作的资源
多个线程访问同一资源的时候,如果处理不当会产生数据错误
3个线程卖票程序,会出现多张同号的票
class MyThread implements Runnable {
private int ticket = 10;
@Override
public void run() {
while (true) {
if (this.ticket > 0) {
System.out.println(
Thread.currentThread().getName()
+ "卖第" + this.ticket + " 张票"
);
this.ticket--;
} else {
System.out.println("票卖光了");
break;
}
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread thread = new MyThread();
new Thread(thread).start();
new Thread(thread).start();
new Thread(thread).start();
// 5
}
}
15 线程同步处理
同步:多个操作在同一时间段内只能有一个线程进行,
其他线程要等待此线程完成之后才可以继续还行
解决同步问题的方式是锁
synchronized定义同步方法或同步代码块,里边的代码只允许一个线程执行
加入同步之后,程序整体性能下降了
1、同步代码块
synchronized(同步对象){}
举例
synchronized (this) {
if (this.ticket > 0) {
System.out.println(Thread.currentThread().getName() +
"卖第" + this.ticket + " 张票");
this.ticket--;
} else {
System.out.println("票卖光了");
break;
}
}
2、同步函数
public synchronized boolean method(){}
举例
public synchronized boolean sale(){
if (this.ticket > 0) {
System.out.println(Thread.currentThread().getName() +
"卖第" + this.ticket + " 张票");
this.ticket--;
return true;
} else {
System.out.println("票卖光了");
return false;
}
}
16 线程死锁
死锁是在进行多线程同步处理之中有可能产生的一种问题
是指若干个线程彼此互相等待的状态
若干线程访问同一资源时,一定要进行同步处理
而过多的同步会造成死锁
public class Demo {
public static void main(String[] args) {
//o1 o2 代表资源
Object o1 = new Object();
Object o2 = new Object();
System.out.println("go go go!");
Thread t1 = new Thread(new Runnable() {
public void run() {
synchronized (o1) { //线程t1获取o1的锁才能继续执行
try {
Thread.sleep(3000); //睡3秒,确保线程t2把o2锁拿走
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1获得了哦O1");
synchronized (o2) { //线程t1获取o2的锁才能继续执行
System.out.println("t1获得了哦O2");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
synchronized (o2) { //线程t2获取o2的锁才能继续执行
try {
Thread.sleep(3000); //睡3秒,确保线程t1把o1锁拿走
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2获得了哦O2");
synchronized (o1) { //线程t2获取o1的锁才能继续执行
System.out.println("t2获得了哦O1");
}
}
}
});
t1.start();
t2.start(); //启动线程
}
}
第4 章 : 综合实战:“生产者-消费者”模型
17 生产者与消费者基本程序模型
生产者负责信息内容生产
消费者取走信息
消费者要等待生产者生产完成再取走
生产者需要等待消费者消费完成再生产
不加锁示例
class Message {
private String content;
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
class Producer implements Runnable {
private Message message;
private static int count;
public Producer(Message message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.message.setContent("这是第" + count + " 个消息");
count++;
}
}
}
class Consumer implements Runnable {
private Message message;
public Consumer(Message message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.message.getContent());
}
}
}
class Demo {
public static void main(String[] args) {
Message message = new Message();
new Thread(new Producer(message)).start();
new Thread(new Consumer(message)).start();
}
}
/**
这是第0 个消息
这是第0 个消息
这是第1 个消息
这是第2 个消息
这是第3 个消息
这是第4 个消息
这是第5 个消息
这是第6 个消息
这是第7 个消息
这是第8 个消息
*/
18 解决生产者-消费者同步问题
增加关键字 synchronized
19 利用Object类解决重复操作
等待机制
(1)一直等待
public final void wait()
(2)等待一段时间
public final native void wait(long timeout)
唤醒线程
(1)唤醒一个等待线程, 唤醒第一个等待的线程
public final native void notify();
(2)唤醒全部等待线程,谁优先级高谁先执行
public final native void notifyAll();
完整代码
class Message {
private String content;
private boolean flag = false; // 生产完成就为true
public synchronized void setContent(String content) {
if (this.flag == true) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content = content;
this.flag = true;
notify();
}
public synchronized String getContent() {
if (this.flag == false) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
return content;
} finally {
this.flag = false;
notify();
}
}
}
class Producer implements Runnable {
private Message message;
private static int count;
public Producer(Message message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
this.message.setContent("这是第" + count + " 个消息");
count++;
}
}
}
class Consumer implements Runnable {
private Message message;
public Consumer(Message message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.message.getContent());
}
}
}
class Demo {
public static void main(String[] args) {
Message message = new Message();
new Thread(new Producer(message)).start();
new Thread(new Consumer(message)).start();
}
}
第5 章 : 多线程深入话题
20 优雅的停止线程
已废除的方法,可能会导致线程死锁,不建议使用
// 停止线程
public final void stop()
// 销毁线程
public void destroy()
// 挂起线程
public final void suspend()
// 恢复线程
public final void resume()
使用flag 标志位不会立刻停止,而是当前线程自己判断
class Demo{
private static boolean flag = true;
public static void main(String[] args) {
new Thread(()->{
while (flag){
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在执行");
}
}, "自定义线程").start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("时间到");
flag = false;
}
}
21 后台守护线程
守护线程,如果主线程退出,守护线程就退出
GC就是守护线程
设置为守护线程
public final void setDaemon(boolean on)
判断是否为守护线程
public final boolean isDaemon()
示例
设置线程为守护线程后,主程序执行完毕就退出了,并不会打印任何内容
class MyThread implements Runnable{
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 正在执行");
}
}
class Demo{
public static void main(String[] args) {
Thread t = new Thread(new MyThread());
t.setDaemon(true);
t.start();
}
}
22 volatile关键字
volatile 用于属性定义, 中文意思:易变的
变量处理的步骤:
(1)获取变量原有的数据内容副本
(2)利用副本为变量进行数学计算
(3)建计算后的变量,保存到原始空间中
读取read <- 数据副本
加载load
使用use
赋值asign
存储store
写入write -> 原始空间
属性上加了volatile, 没有中间拷贝过程,直接使用原始数据
区别:volatile 和 synchronized
volatile: 主要在属性上使用,无法描述同步,直接内存处理,避免副本操作
synchronized: 代码块与方法上使用
class MyThread implements Runnable{
private volatile int count = 10;
@Override
public void run() {
while (count>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 正在执行");
count --;
}
}
}
class Demo{
public static void main(String[] args) {
Thread t = new Thread(new MyThread());
t.start();
}
}