今天复习了java中Thread类的使用,以及使用synchronized关键字对对象上锁。以及wait/notify进行线程间的同步。
实现自己的线程类有2种方法,一种是extends Thread类,然后重写run方法。
第二种是实现Runnable接口,并implements run方法。
Thread类中有一个成员叫做Target,是一个Runnable的引用。当我们使用无参数的构造方法时,这个target是赋成null的。如果使用一个实现Runnable的类的对象来new一个Thread,那么这个对象的引用就会传给target的,Thread的run方法就是调用这个target的run方法。
所以,如果使用继承Thread的方法来实现自己的线程类,那么就要重写run方法,否则它什么都不做,因为此时target是null。
Synchronized的关键字
可以作用于方法,也可以作用于代码块。
每个object都有一把锁,当有线程调用被synchronized修饰的非static方法时,该object会被上锁,直到调用结束,释放锁之后,其他线程才可调用该object中synchronized修饰的非static方法.
如果是static的方法被修饰,那么是对应的class类的对象被上锁,该对象唯一。
使用synchronized代码块的好处是,首先,更精确,仅把必要的代码列入临界区间。还有就是,可以为不同的代码块使用不同的锁。
点击(此处)折叠或打开
- Synchronized(mutex_1){
- critical section_1
- }
- Synchronized(mutex_2){
- critical section_2
- }
wait/notify
首先明确的一点是,这两个方法是Object提供的,而不是Thread类提供的。
最重要的一句话时,The current thread must own this object's monitor.
即当前线程必须是该对象monitor的持有者,换句话说,只要在synchronized修饰的方法或代码块中才能使用这两个方法,否则,会抛出IllegalMonitorStateException
举个例子
点击(此处)折叠或打开
- class Test{
- private Object mutex = new Object();
- public void Fun_1{
- Synchronized(mutex){
- while(some condition){
- mutex.wait();
- }
- }
- }
- }
这里调用wait时,一定是mutex的,如果调用的是this的wait方法,那么就会出现异常,原因是当前线程没有持有this的锁。
做了个小练习,有一个栈,有生产者和消费者,生产者负责将26个英文字母依次push到栈中,消费者则进行pop操作。栈的大小是10.我假设一个生产者,2个消费者。
点击(此处)折叠或打开
- package threadCommunication2;
- public class Stack {
- private final int MAX = 10;//栈的大小是10
- private char[] buffer;//为了简单,我就用了个数组
- private int top;//栈顶
- public Stack() {
- this.buffer = new char[MAX];
- this.top = -1;
- }
- public void push(char c) {
- synchronized (this) {
- while (top == MAX-1) {//当栈满了时,要等消费者来唤醒
- System.out.println("buffer is full...");
- try {
- wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- buffer[++top] = c;
- try {
- Thread.sleep((long) (Math.random() * 100));//随机延迟
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(c + " is pushed");
- notify();
- }
- }
- public char pop() {
- synchronized (this) {//栈空时,被wait阻塞,等待生产者唤醒
- while (top == -1) {
- System.out.println("buffer is empty...");
- try {
- wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- char tmp = buffer[top];
- top--;
- try {
- Thread.sleep((long) (Math.random() * 1000));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- notify();
- return tmp;
- }
- }
- }
点击(此处)折叠或打开
- package threadCommunication2;
- public class PushThread extends Thread {
- private Stack s;
- public PushThread(Stack s) {
- this.s = s;
- }
- public void run() {
- int i = 'a';
- while (i != 'z'+1) {
- s.push((char) i++);
- }
- }
- }
点击(此处)折叠或打开
- package threadCommunication2;
- public class PopThread extends Thread {
- private Stack s;
- public PopThread(Stack s) {
- this.s = s;
- }
- public void run() {
- int i = 0;
- while (i < 13) {
- System.out.println(s.pop());
- i++;
- }
- }
- }
这个pop比较囧。。。13是事先算的,一个消费者取出13个。。。因为主要是复习同步,所以这些暂且这么样吧。
点击(此处)折叠或打开
- package threadCommunication2;
- public class Main {
- public static void main(String[] args) {
- Stack stack = new Stack();
- Thread push = new PushThread(stack);
- Thread pop = new PopThread(stack);
- Thread pop1 = new PopThread(stack);
- push.start();
- pop.start();
- pop1.start();
- }
- }