Java多线程——(1)多线程基础

1.线程的基本操作

线程共有5个状态:

NEW(新建状态)

RUNNABLE(可运行态)

TERMINATED(线程终止)

BLOCKED (阻塞) ,进入同步块时申请监视器导致的阻塞

WAITING (无限等待)

TIMED_WAITING(有限等待)

1.1新建线程

 常见有两种创建线程的方法:

1)继承Thread类重写run()方法。

2)继承Runnable接口。在构造时传入具体实现对象。

如果没有传递target实例,那么run方法内部就不会执行任何逻辑。

如果传递了tartget,那么run方法便会执行target实例中的run方法。

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }    
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    .....
    /* What will be run. */
    private Runnable target; 

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

要注意的是,如果没有执行start()方法开启线程,而是直接在主线程中调用一个线程的run()方法,结果只会在当前线程调用run()方法,而并没有实质性地开启新线程。

1.2 终止线程——stop()

 Thread.stop()是一个被弃用的方法。主要原因是线程的突然终止会导致数据的不一致。太过于粗暴。

    @Deprecated
    public final void stop() {
      ....   
    }

1.3 中断线程——interrupt()

public void Thread.interrupt() //中断线程
public boolean Thread.isInterrupted() //判断是否被中断
public static boolean Thread.interrupted() //判断是否被中断,并清除当前中断状态
public void run(){
    while(true){
        //doSomething.....
    }
}

t1.interrupt(); //发出中断命令,而并未做出响应。所以while循环会继续执行下去

/////////////////////////////////////////////////////////////////////////////////

public void run(){
    while(true){
        if(Thread.currentThread.isInterrupted()){
            System.out.println("Interruted!");
            break;
        }
        //doSomething...
    }
}

t1.interrupt(); //发出中断命令,while循环中会判断出现中断,退出循环

////////////////////////////////////////////////////////////////////////////
但若是doSomething中出现异常时候,要防范抛出异常时,中断标记位会被清空。要重新中断
public void run(){
    while(true){
        if(Thread.currentThread.isInterrupted()){
            System.out.println("Interruted!");
            break;
        }
        try{
            Thread.sleep(2000);
        }catch(InterruptedException e){
            System.out.println("Interrupted When Sleep");
            //由于抛出异常后会清除中断标记,所以再次设置中断标记,以被if检测到
            Thread.currentThread().interrupt()
        }
        //doSomething...
    }
}

1.4  挂起(suspend) 和继续执行(resume) 线程

这两个操作也是不被推荐使用的。两个线程无法控制顺序时,当resume()在suspend()执行之前就已经执行,所以此后被挂起的线程便再也不能被唤醒了,而suspend()是不会释放锁的,导致等待在这个锁上的其他线程会持续等待下去。

1.5 等待线程结束(join)和谦让(yeild)

 join() 表示等待前一个线程执行完毕后再执行,也有叫“线程插队”

public class JoinMain{
	public volatile static int i = 0;
	public static class AddThread extends Thread{
		@Override
		public void run() {
			for(i=0; i < 10000000; i++);
		}
	}
	public static void main(String args[]) throws InterruptedException{
		AddThread at = new AddThread();
		at.start();
		at.join();       //主线程会等待at线程执行完毕后再一起走
		System.out.println(i);
	}
	
}

/////////////////////////////////////////////////
join的本质:
while(isAlive()){
    wait(0);
}
线程执行完毕会调用notifyAll()通知等待的所有线程

yeild会让出时间片,但不是白给,而是再和别的线程竞争。”再给对方一次竞争机会“。

public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException//一般用于测试目的

2. 守护线程&线程优先级

当一个Java应用内,只有守护线程时,JVM就会自然退出。

java 中的线程优先级的范围是1~10,默认的优先级是5。高优先级的线程更容易在竞争中获胜。

package Thread;
/**
 * 后台线程
 * @author liq
 *
 */
public class DamonThread {
	
	class Damon implements Runnable{

		@Override
		public void run() {
			while(true) {
				try {
				Thread.sleep(1000);
					System.out.println(Thread.currentThread().getName()+" is running");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	
	public static void main(String[] args) {
		Damon damon = new DamonThread().new Damon();
		Thread damonThread = new Thread(damon, "后台线程");
		damonThread.setPriority(10);  //在start之前设置优先级
		damonThread.setDaemon(true);
		System.out.println("'damonThread' is a Damon Thread?"+damonThread.isDaemon());
		damonThread.start();
		for(int i=0; i<10; i++) {
			try {
				Thread.sleep(1000);
				System.out.println("Main Thrad is Running"+i);
				if (i == 9) {
					System.out.println("Main Thrad is dead");
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
		}
	}
}

3. 基本的线程同步操作——synchronized关键字&wait(),notify()

3.1 synchronized线程同步

-指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。

-直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。

-直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。

下面进行

/**
 * synchronized代码块
 * @author liq
 *
 */
public class SynchronizedBlock {
	
	public static void main(String[] args) {
		Ticket ticketTask = new Ticket();
		new Thread(ticketTask,"售票窗口1").start();
		new Thread(ticketTask,"售票窗口2").start();
		new Thread(ticketTask,"售票窗口3").start();
		new Thread(ticketTask,"售票窗口4").start();
	}
	
}


class Ticket implements Runnable{

	private int ticketsNum = 10; //总共十张票
	Object lock = new Object();
	@Override
	public void run() {
		
		while(true) {
			synchronized (lock) {
				try {
					if (ticketsNum > 0) {
						System.out.println(Thread.currentThread().getName()+"售票,现存:"+ --ticketsNum);
						Thread.sleep(1000);
					}else {
						System.out.println("票售完了!");
						break;
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
}
锁静态方法,就是对当前类加锁
class Ticket implements Runnable{

	private static int ticketsNum = 10; //总共十张票
	Object lock = new Object();
	@Override
	public void run() {
		sellTickt();
	}
	
	public static synchronized void sellTickt() { //锁静态方法,就是对当前类加锁
		while(true) {
			try {
				if (ticketsNum > 0) {
					System.out.println(Thread.currentThread().getName()+"售票,现存:"+ --ticketsNum);
					Thread.sleep(1000);
				}else {
					System.out.println("票售完了!");
					break;
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

 3.2 wait()和notify()实现线程同步

需要注意:

1)wait和notify方法必须在同步代码块中执行,即在执行前需要拿到锁。

2)wait()会释放锁。

3)notify也会释放锁。

notify()是在同步队列中随机唤醒一个线程

notifyAll()是唤醒全部开始竞争锁。

实现生产者消费者例子:


/**
 * This demon shows the co-operation between two threads of Input and Output
 * @author liq
 *
 */
public class WaitAndNotify {

	public static void main(String[] args) {
		Storage storage = new Storage();
		Input input = new Input(storage);
		Output output = new Output(storage);
		new Thread(input,"input线程").start();
		new Thread(output,"output线程").start();
	}
	
}

//this is Input thread which is used to play a producer role to put a number in storage per second  
class Input implements Runnable{

	Storage storage;
	int num;
	
	public Input(Storage storage) {
		this.storage = storage;
	}

	@Override
	public void run() {
		while(true) {
			try {
				Thread.sleep(1000);
				storage.put(num++);
				System.out.println("producer puts num:"+num);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

//this is Output thread which is used to play a consumer role to get a number in storage per second  
class Output implements Runnable{

	Storage storage;
	
	public Output(Storage storage) {
		this.storage = storage;
	}

	@Override
	public void run() {
		while(true) {
			try {
				Thread.sleep(1000);
				System.out.println("------------------------consumer gets num:"+storage.get());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

//this is a mutual storage of consumer and producer thread to storage number
class Storage{

	private int[] cells = new int[10];
	int size = 0; //实际存储容量
	int inPos,outPos;
	
	//往cells中放入一个数
	public synchronized void put(int in) {
		try {
				if (size == 10) {    //若存储已满,当前线程等待
					this.wait();
				}
				cells[inPos++] = in;
				size++;
				//超过10位置重新从0开始存放
				if (inPos == 10) {
					inPos=0;
				}
				this.notifyAll();   //通知在此同步锁上等待的其他线程(消费者)开始执行
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	
	//从cells中取出一个数
	public synchronized int get() {
		int result = 0;
		try {
				//Storage 为空了,线程等待
				if (size == 0) {
					this.wait();       //若存储已空,当前线程等待。
				}
				
				result = cells[outPos++];
				size--;
				//超过大小,从0开始取出
				if (outPos == 10) {
					outPos = 0;
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return result;
		}
		
	}

猜你喜欢

转载自blog.csdn.net/fantalee/article/details/81386897