线程
前述:终于要到线程了,学完线程和网络编程,就可以开始下一个阶段了。
线程同步
- 理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,就需要用到“线程同步”。
- 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。
- 同一进程的多个线程共享同一块存储空间,有访问冲突的问题。
- Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。
- 解决机制:添加上synchronized关键字,它包括两种用法。
- synchronized 方法
- 控制对“对象的类成员变量”的访问:每个对象对应一把锁。
- 每个 synchronized方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞。
- 方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。
- synchronized 块
- 通过 synchronized关键字来声明synchronized 块。
- 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率。
- synchronized 方法
看下面一段代码:
public class TestSync {
public static void main(String[] args) {
apples a1 = new apples(10, "苹果");
deleteA d1 = new deleteA(6, a1); // 新建线程对象
deleteA d2 = new deleteA(6, a1);
d1.start(); // 线程开始
d2.start();
}
}
class apples {
int nums; // 储存的苹果数
String name; // 储存名
public apples(int nums,String name) {
super();
this.name = name;
this.nums = nums;
}
}
// 实现线程的减苹果方法
class deleteA extends Thread{
int count; // 分苹果的数量
apples a; // 苹果所有类
int allcounts = 0; // 分苹果苹果总数
public deleteA(int count,apples a) {
this.a = a;
this.count = count;
}
public void run(){
// 判断线程停止
if(a.nums-count<0) {
System.out.println("线程停止了!");
return ;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
a.nums -= count;
allcounts += count;
System.out.println(this.getName()+"剩余苹果个数:"+a.nums);
System.out.println(this.getName()+"共分了苹果:"+allcounts);
}
}
控制台输出:
Thread-1剩余苹果个数:-2
Thread-1共分了苹果:6
Thread-0剩余苹果个数:-2
Thread-0共分了苹果:6
以上是线程不同步的示例,其带来的影响是很大的,线程没有在判断处停下来。原始苹果只有10个,却分出去12个,这显然是不合理的。
接下来看一个加上同步的例子:
public class TestSync {
public static void main(String[] args) {
Account a1 = new Account(100, "账户存款");
Drawing draw1 = new Drawing(80, a1);
Drawing draw2 = new Drawing(80, a1);
draw1.start(); // 你取钱
draw2.start(); // 你老婆取钱
}
}
/*
* 简单表示银行账户
*/
class Account {
int money;
String aname;
public Account(int money, String aname) {
super();
this.money = money;
this.aname = aname;
}
}
/**
* 模拟提款操作
*
* @author Administrator
*
*/
class Drawing extends Thread {
int drawingNum; // 取多少钱
Account account; // 要取钱的账户
int expenseTotal; // 总共取的钱数
public Drawing(int drawingNum, Account account) {
super();
this.drawingNum = drawingNum;
this.account = account;
}
@Override
public void run() {
draw();
}
void draw() {
synchronized (account) {
if (account.money - drawingNum < 0) {
System.out.println(this.getName() + "取款,余额不足!");
return;
}
try {
Thread.sleep(1000); // 判断完后阻塞。其他线程开始运行。
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= drawingNum;
expenseTotal += drawingNum;
}
System.out.println(this.getName() + "--账户余额:" + account.money);
System.out.println(this.getName() + "--总共取了:" + expenseTotal);
}
}
查看控制台输出:
//第一批次运行
Thread-1取款,余额不足!
Thread-0--账户余额:20
Thread-0--总共取了:80
//第二批次运行
Thread-0--账户余额:20
Thread-1取款,余额不足!
Thread-0--总共取了:80
这样就实现了线程的同步,并且银行账户也没有发生取钱超额的情况。
线程并发协作(生产者/消费者模式)
比较难掌握的一个点,不是特别理解,但还是觉得该记录了下来。