1.线程的基本操作
(1)创建线程实现接口runnable 或者extends Thread
(2)线程启动run()
(3)线程终止
暴力:thread.interrupt();thread.stop();
正常停止线程:设置标志位,只有flag=true其中的run方法才会执行
package ProductAndConsume;
class ThreadA extends Thread{
boolean flag = false;
public synchronized boolean getFlag() {
return flag;
}
public synchronized void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
while(!getFlag()){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("run....");
}
}
}
public class TerminateThread {
public static void main(String[] args) throws InterruptedException {
ThreadA A = new ThreadA();
A.start();
Thread.sleep(1000);
A.setFlag(true);
}
}
2.线程控制:Sleep/Join/Yield方法
sleep进入睡眠状态,但是并不释放cpu;
join是线程合并,既然合并了那就变成一个线程就有先后执行顺序,也就是让某个线程先执行完毕
yield让出cpu权限,给其他线程机会
Sleep/Join(需要捕获异常)
3.线程同步
(1)互斥
问题引入:两人同时取同一账户的钱两个线程访问同一资源,进程之间协调的问题
解决方案:在进程访问独占资源时先锁定再访问。Synchronized可以先锁定一个对象,使用方式为synchronized(Object),其中Object可以是this(当前这个对象),当是this时可以简写在函数声明处。等同于:public synchronized void add()...
(2)同步
XXX.wait()让当前访问这个线程等待,此时这个线程的锁不存在了
XXXX.notify()唤醒在此对象监视器上等待的单个线程。
wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能.因为都个对像都有锁,锁是每个对像的基础,当然操作锁的方法也是最基础了。
wait导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法,或被其他线程中断。Wait只能由持有对像锁的线程来调用。notify唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程(随机)。直到当前的线程放弃此对象上的锁,才能继续执行被唤醒的线程。同Wait方法一样,notify只能由持有对像锁的线程来调用.notifyall也一样,不同的是notifyall会唤配所有在此对象锁上等待的线程。
"只能由持有对像锁的线程来调用"说明wait方法与notify方法必须在同步块内执行,即synchronized(obj)之内.再者synchronized代码块内没有锁是寸步不行的,所以线程要继续执行必须获得锁。
//标准模式
synchronized(obj){
while(!condition) {
obj.wait();
}
obj.doSomething();
}
当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait(),放弃对象锁.之后在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:
synchronized(obj){
condition = true;
obj.notify();
}
需要注意的概念是:
(1)调用obj的wait(),notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj){…}代码段内。
(2)调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj){…}代码段内唤醒A。
当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁而且要符合while中的等待条件。当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的才有机会获得锁继续执行。
4.生产者消费者实例:
package com.zgd.thread;
class Baozi{
private static int num=0;
int id = 0;
public Baozi() {
super();
num++;
id=num;
}
@Override
public String toString() {
return "Baozi :[id=" + id + "]";
}
}
class MyStack{
private Baozi[] queue=new Baozi[6];
int len=6;
int cur=0;
public void push(Baozi baozi){
synchronized (queue) {
while(cur == len){
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue[cur++] = baozi;
queue.notifyAll();
}
}
public Baozi pop(){
synchronized (queue) {
while(cur == 0){
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Baozi b = queue[--cur];
queue.notify();
return b;
}
}
}
class Consume implements Runnable{
private MyStack stack = null;
private String name=null;
public Consume(String name, MyStack stack) {
super();
this.stack = stack;
this.name = name;
}
@Override
public void run() {
for(int i=1; i<=6; i++){
System.out.println(name + " consume:" + stack.pop());
// Thread.yield();
}
}
}
class Product implements Runnable{
private MyStack stack = null;
private String name=null;
public Product(MyStack stack, String name) {
super();
this.stack = stack;
this.name = name;
}
@Override
public void run() {
for(int i=1; i<=6; i++){
Baozi b = new Baozi();
System.out.println(name + " produce:"+b.toString());
stack.push(b);
// Thread.yield();
}
}
}
public class ConsumeProduct {
public static void main(String args[]){
MyStack stack = new MyStack();
new Thread(new Consume("consume1",stack)).start();
new Thread(new Consume("consume2",stack)).start();
new Thread(new Product(stack, "producer1")).start();
new Thread(new Product(stack, "producer2")).start();
}
}
5.关于多线程调度的几条规则:
(1)同步块中应尽可能简单,不要调用一个可被改写的公有或受保护方法。因为这个方法不受控制,如果客户在这个方法内创建另一个线程,而新建的线程试图获取原线程所持有的对象锁,那么新建的线程就会被阻塞,而创建该线程的方法需要等待其完成,就会形成死锁
package ProductAndConsume;
class T {
private Object o1 = new Object();
public void t1() {
synchronized (o1) {
System.out.println("in t1...");
}
}
public void test() throws InterruptedException{
//new Thread -> call t1
Thread child = new Thread(){
public void run(){
System.out.println("in child thread....");
t1();
}
};
child.start();
child.join();
}
public void t2() throws InterruptedException {
synchronized (o1) {
test();
System.out.println("in t2...");//永远打印不出来。。
}
}
}
public class NewTest {
public static void main(String[] args) throws InterruptedException {
T tt = new T();
tt.t2();
}
}
(2)永远不要在循环外面调用wait,调用wait的标准模式:
synchronized (obj) {
while (!condition) {
obj.wait();
}
obj.doSomething();
}
(3)关于线程安全级别
非可变 线程安全的 有条件线程安全的(Hashtable和Vector) 线程兼容的 (不安全的,如HashMap)
6.相关的学习资料:
http://lavasoft.blog.51cto.com/62575/27069
http://www.ibm.com/developerworks/cn/java/j-concurrent/
http://www.ticmy.com/?p=97