Java基础--线程(2)

来源:互联网 发布:淘宝费用预算 编辑:程序博客网 时间:2024/06/01 13:19
1.线程的优先级别​
java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度那个线程来执行。​
线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5​
Thread.MIN_PRIORITY=1,Thread.MAX_PRIORITY=10,Thread.NORM_PRIORITU=5,​
使用下述方法获得或设置线程对象的优先级。​

int getPriority();    

void setPriority(int newPriority);​​

例如:​
public class TestPriority {    public static void main(String args[]) {        Thread t1 = new Thread(new T1());        Thread t2 = new Thread(new T2());        t1.setPriority(Thread.NORM_PRIORITY + 3);        t1.start();        t2.start();    }}class T1 implements Runnable {    public void run() {        for (int i = 0; i < 1000; i++) {            System.out.println("T1 " + i);        }    }}class T2 implements Runnable {    public void run() {        for (int i = 0; i < 1000; i++) {            System.out.println("--------T2: " + i);        }    }}

结果:T1和T2并行运行,但是T1的运行几率比较大(比如运行T1到600然后运行几次T2,只有继续运行T1),并不是T1运行完在运行T2,如果不设置优先级,T1和T2间隔基本相同
2.线程同步​
例:一存折和一银行卡账户上有3000元,当存折在柜台上取2000时,系统检测到有3000,银行工作人员在给你钱但没有更新数据时,另一个人在取款机上用银行卡取2000,系统将数据更新成1000,银行工作人员,办完业务也将数据更新成1000,这样就出现了错误。​
银行卡和存折就相当于两个线程,解决上述问题,就是在一个线程访问数据时,不允许其他线程访问。访问同一份资源的多个线程之间来进行协调,叫做线程同步。​​
线程同步​
在java语言中,引入了对象互斥锁的概念,保证共享数据操作的完整性。每个对象都对应于一个可称为互斥锁的标记,这个标记保证在任何时刻,只能又一个线程访问该对象。关键字synchronized来与对象的互斥锁联系,当某个对象synchronized修饰时,表明该对象在任一时刻只能由一线程访问,使用方法如下:
public class TestSync implements Runnable {    Timer timer = new Timer();    public static void main(String args[]) {        TestSync test = new TestSync();//test指向new出来的testSync,testSync        //中有timer对象,timer有add方法,t1,t2两个线程都访问了add方法        Thread t1 = new Thread(test);        Thread t2 = new Thread(test);        t1.setName("t1");        t2.setName("t2");        t1.start();        t2.start();    }    public void run() {        timer.add(Thread.currentThread().getName());    }}class Timer {    private static int num = 0;    public void add(String name) {        synchronized (this) {//锁定当前对象            num++;            try {                Thread.sleep(1);            } catch (InterruptedException e) {            }            System.out.println(name + ",你是第" + num + "个使用timer的线程");        }    }}

结果是:

t1,你是第1个使用timer的线程   

t2,你是第2个使用timer的线程


如果去掉synchronized (this)的话,结果会是t1,你是第2个使用timer的线程 t2,你是第2个使用timer的线程,因为在t1访问num++之后进入睡眠,这时num=1,然后t2就能访问num++,此时num=2,t1和t2继续运行就会产生2个2,​所以为了避免这中情况发生加入synchronized(this)即是将当前对象锁定,在t1访问时,t2不能访问​
timer类也可写成:​
class Timer {    private static int num = 0;    public synchronized void add(String name) {        //synchronized (this) {//锁定当前对象        num++;        try {            Thread.sleep(1);        } catch (InterruptedException e) {        }        System.out.println(name + ",你是第" + num + "个使用timer的线程");    }}
另一个例子:​
public class TT implements Runnable {    int b = 100;    public synchronized void m1() throws Exception {        b = 1000;        Thread.sleep(5000);        System.out.println("b = " + b);    }    public void m2(){            System.out.println(b);    }    public void run() {        try {            m1();        } catch (Exception e) {            e.printStackTrace();        }    }    public static void main(String[] args) throws Exception {        TT tt = new TT();        Thread t = new Thread(tt);        t.start();        Thread.sleep(1000);        tt.m2();    }}

结果是:1000  b=1000​
虽然tt对象是锁定的,但是锁定的意义是只是锁定的m1方法(其他对象不能在访问m1方法),m2方法仍然可以运行,否则就会输出100。​​
将m2方法改成:
public void m2() throws Exception {Thread.sleep(2500);b = 2000;}

并去掉main中的Thread.sleep(1000),并在tt.m2后加System.out.println(tt.b)

结果是:2000  b=2000,m1在睡眠锁定时,m2仍然改掉了b的值,要想解决这个问题,必须要给m2也加上锁,b就等于1000,但实际上b的值是2000​


所以同步的程序,再一个锁定的对象里的值,需要考虑在其他非同步的程序中是否涉及,如果涉及就需要考虑这个方法是否也要锁定。​

在给m2也加同步,并在tt.m2后加System.out,println(tt.b),结果是1000,b=1000,main方法运行中加入了m1将b改为1000,并且m1进入睡眠,此时运行m2,m2也进入睡眠,然后就会打印1000,然后m2唤醒,但是m1,m2都是同步状态,并不能影响到m1的输出,所以输出b=1000.
3.死锁​
所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。​
学过操作系统的朋友都知道:产生死锁的条件有四个:
1.互斥条件:所谓互斥就是进程在某一时间内独占资源。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
例如:死锁是因为多线程访问共享资源,由于访问的顺序不当所造成的,通常是一个线程锁定了一个资源A,而又想去锁定资源B;在另一个线程中,锁定了资源B,而又想去锁定资源A以完成自身的操作,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等待,而无法执行的情况。分析死锁产生的原因不难看出是由访问共享资源顺序不当所造成的,下面写一个造成线程死锁的例子,希望能对大家理解多线程死锁问题有进一步的理解!如果有人需要编写多线程的系统,当操作共享资源时一定要特别的小心,以防出现死锁的情况!
public class TestDeadlock implements Runnable {    public int flag = 1;    static Object o1 = new Object(), o2 = new Object();    public void run() {        System.out.println("flag=" + flag);        if (flag == 1) {            synchronized (o1) {                try {                    Thread.sleep(500);                } catch (Exception e) {                    e.printStackTrace();                }                synchronized (o2) {                    System.out.println("1");                }            }        }        if (flag == 0) {            synchronized (o2) {                try {                    Thread.sleep(500);                } catch (Exception e) {                    e.printStackTrace();                }                synchronized (o1) {                    System.out.println("0");                }            }        }    }    public static void main(String[] args) {        TestDeadlock td1 = new TestDeadlock();        TestDeadlock td2 = new TestDeadlock();        td1.flag = 1;        td2.flag = 0;        Thread t1 = new Thread(td1);        Thread t2 = new Thread(td2);        t1.start();        t2.start();    }}

结果是:flag=0  flag=1然后无限运行,没有出现终止​​
4.生产者消费者问题
public class ProducerConsumer {    public static void main(String args[]) {        SyncStack ss = new SyncStack();        Producer p = new Producer(ss);        Consumer c = new Consumer(ss);        new Thread(p).start();        new Thread(c).start();    }}class WoTou { //定义一个生产出来的对象    int id;    WoTou(int id) {        this.id = id;    }    public String toString() {//对象返回值        return "WuTou :" + id;    }}class SyncStack { //容器使用栈的方式,先进后出    int index = 0;    WoTou[] arrWT = new WoTou[6];    public synchronized void push(WoTou wt) {        while (index == arrWT.length) {            try {                this.wait(); //生产满后等待            } catch (InterruptedException e) {                e.printStackTrace();            }        }        this.notifyAll();        arrWT[index] = wt;        index++;    }    public synchronized WoTou pop() {        while (index == 0) {            try {                this.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        this.notifyAll();        index--;        return arrWT[index];    }}class Producer implements Runnable {    SyncStack ss = null;    Producer(SyncStack ss) {        this.ss = ss;    }    public void run() {        for (int i = 0; i < 20; i++) {            WoTou wt = new WoTou(i);            ss.push(wt);            System.out.println("生产了:" + wt);            try {                Thread.sleep((long) (Math.random() * 1000));            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}class Consumer implements Runnable {    SyncStack ss = null;    Consumer(SyncStack ss) {        this.ss = ss;    }    public void run() {        for (int i = 0; i < 20; i++) {            WoTou wt = ss.pop();            System.out.println("消费了:" + wt);            try {                Thread.sleep((long) (Math.random() * 1000));            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

结果:
生产了:WuTou :0
消费了:WuTou :0
生产了:WuTou :1
消费了:WuTou :1
生产了:WuTou :2
消费了:WuTou :2
生产了:WuTou :3
消费了:WuTou :3
生产了:WuTou :4
生产了:WuTou :5
消费了:WuTou :5
消费了:WuTou :4
生产了:WuTou :6
消费了:WuTou :6
生产了:WuTou :7
生产了:WuTou :8
消费了:WuTou :8
生产了:WuTou :9
消费了:WuTou :9
生产了:WuTou :10
生产了:WuTou :11
生产了:WuTou :12
生产了:WuTou :13
生产了:WuTou :14
生产了:WuTou :15
消费了:WuTou :14
消费了:WuTou :15
生产了:WuTou :16
消费了:WuTou :16
生产了:WuTou :17
消费了:WuTou :17
生产了:WuTou :18
消费了:WuTou :18
消费了:WuTou :13
消费了:WuTou :12
生产了:WuTou :19
消费了:WuTou :19
消费了:WuTou :11
消费了:WuTou :10
消费了:WuTou :7
0 0
原创粉丝点击