线程知识点

一些关于线程学习的记录

Posted by LJJ on April 7, 2019

线程

前述:终于要到线程了,学完线程和网络编程,就可以开始下一个阶段了。

线程同步

  • 理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,就需要用到“线程同步”。
  • 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。
  • 同一进程的多个线程共享同一块存储空间,有访问冲突的问题。
  • Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。
  • 解决机制:添加上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

这样就实现了线程的同步,并且银行账户也没有发生取钱超额的情况。

线程并发协作(生产者/消费者模式)

比较难掌握的一个点,不是特别理解,但还是觉得该记录了下来。

Java多线程之并发协作生产者消费者设计模式