Java基础13--多线程
来源:互联网 发布:23种设计模式java 编辑:程序博客网 时间:2024/06/01 20:18
13-1,线程的状态
1,Thread的stop()方法能强制结束线程。
2,两个概念(非专业术语):
CPU的执行资格:可以被CPU处理,在处理队列中排队;
CPU的执行权:正在被CPU处理。
线程的执行过程:
线程先被创建,然后用start方法运行,此时具备执行资格和执行权,运行完成后消亡。在运行时线程可以临时冻结,此时释放执行资格和执行权,由于CPU在同一时间只能运行一个线程,所以除了正在执行的线程,其他线程都处于临时阻塞状态,此时具备执行资格但不具备执行权。
3,线程状态图示:
线程的状态:被创建、运行、冻结、消亡、临时阻塞状态,横线为重点掌握。
13-2,创建线程的第二种方式:
1,第二种方式:实现Runnable接口。
步骤:
(1)定义类实现Runnable接口。
(2)覆盖接口中的run方法,将线程的任务代码封装到run方法中。
(3)通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。为什么呢?因为线程的任务都封装在Runnable接口的子类对象的run方法中,所以要在对象创建时就必须明确要运行的任务。
(4)调用线程对象的start方法开启线程。
2,当一个类已经有父类时,就不能通过Thread类来实现多线程了,只能通过实现Runnable接口来实现多线程。
3,实现Runnable接口的代码示例:
//准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行,通过接口的形式来完成。
class Demo implements Runnable {public void run() { //重写run方法show();}public void show() {for(int x=0;x<20;x++) {System.out.println(Thread.currentThread.getName()+ "..." + x);}}}class ThreadDemo {public static void main(String[] args) {Demo d = new Demo();/*Thread类创建线程对象,将Runnable的子类Demo的对象作为Thread类的构造函数的参数进行传递。*/Thread t1 = new Thread(d);Thread t2 = new Thread(d);//调用线程对象的start方法开启线程。t1.start();t2.start();}}
13-3,多线程-第二种实现方式细节
为什么把Runnable的子类对象作为参数传递给Thread类的构造函数,执行的就是子类中的run方法呢?
下面通过模拟Thread类的实现来说明。看下列代码:
class Thread {private Runnable r;Thread() {}Thread(Runnable r) {this.r = r;}public void run() {if(r != null) {r.run();}}public void start() {run();}}class ThreadImpl implements Runnable {public void run() {System.out.println("runnable run");}}class Demo {public static void main(String[] args) {ThreadImpl i = new ThreadImpl();Thread t = new Thread(i);t.start();}}
Thread类中有一个Runnable的引用r,当把子类对象传入时,这个r就指向子类对象,run方法中判断如果传入了参数,就执行子类中的run方法,否则什么也不做。
13-4,第二种实现方式的好处
好处:
(1)将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务封装成对象。
(2)避免了Java单继承的局限性。
所以创建线程的第二种方式较为常用。Runnable的出现仅仅是将线程的任务进行了对象的封装。
13-5,多线程-练习-买票
用多线程模拟:游乐园有四个售票口,一共买一百张票。
分析:每个售票口都是一个单独的线程,随机的售票,一个售票口的关闭不会影响其他窗口售票。
实现:
class Ticket implements Runnable {private int num = 100;//票的总数public void run() {while(true) {if(num > 0) { //票数不能是负数System.out.println(Thread.currentThread().getName()+ "..sale.." + num--);}}}}class TicketDemo {public static void main(String[] args) {Ticket t = new Ticket();Thread t1 = new Thread(t);Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);t1.start();t2.start();t3.start();t4.start();}}
13-6,线程安全问题现象
以13-5中的例子来说。
class Ticket implements Runnable {private int num = 100;//票的总数public void run() {while(true) {if(num > 0) { //票数不能是负数System.out.println(Thread.currentThread().getName()+ "..sale.." + num--);}}}}
这个例子中,开启了四个线程,Thread-0到Thread-3,此时若num=1,run方法执行,if(num>0)成立,但Thread-0线程读完if语句后没有执行Sop打印,CPU切到别的线程执行,这时比如切到了Thread-1,Thread-1线程也只读了if语句,因为这时num还等于1,所以if成立,这时CPU又切到了其他的Thread线程,Thread-2与Thread-3也是一样。然后这些线程都只有执行资格但不具备执行权,CPU又切回Thread-0线程,然后执行Thread-0的打印输出了,打印Thread-0..sale..1,然后num--,这时num=0,由于Thread-1的if已经执行,所以不再判断,直接执行Thread-1的Sop输出,打印Thread-1..sale..0,Thread-2和Thread-3同样,打印出Thread-2..sale..-1和Thread-3..sale..-2,这样就出现了不期望出现的结果,产生了安全问题。
13-7,线程安全问题产生的原因
1,产生原因:上例中由于num是在堆中的共享数据,当执行完if而没有执行sop,CPU切到别的线程,再返回该线程,num的值就可能产生变化,而此时将不再执行if,直接执行sop,但此时的结果可能不是预想的结果了。
如果执行完if马上执行sop就不会出现这个问题了。
2,线程安全问题产生的原因:
(1)多个线程在操作共享的数据。
(2)操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致安全问题的产生。
13-8,同步代码块—解决安全问题
1,解决思路:
就是将多条操作共享数据的线程代码封装起来,当有现成在执行这些代码的时候,其他线程是不可以参与运算的,必须要当前线程把这些代码都执行完毕,其他线程才可以参与运算。
2,在Java中,用同步代码块就可以解决这个问题。
同步代码块的格式:
synchronized(对象) {
需要被同步的代码...
}
示例:
class Ticket implements Runnable {private int num = 100;Object obj = new Object();public void run() {while(true) {synchronized(obj) {if(num > 0) {try {Thread.sleep(10);}catch(InterruptedException e) {}System.out.println(Thread.currentThread().getName() + "...sale..." + num--);}}}}}
13-9 ,多线程-同步的好处和弊端
1,同步代码块解决安全问题的原理
synchronized(obj) { //obj是锁,可以理解为1表示开锁,0表示关锁if(num > 0) {try {Thread.sleep(10);}catch(InterruptedException e) {}System.out.println(Thread.currentThread().getName() + "...sale..." + num--);}}
当线程1到同步代码块时,obj为1,即此时线程1可以进入执行代码,而线程1一进入,锁就置为0,关闭锁,就算线程1在里面sleep,别的线程也无法进入。一旦线程1执行完,锁再从0置为1,开锁,这时别的线程就可以进入了,其他线程的原理也是如此。
2,同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步的线程都会判断同步锁。
13-10,多线程-同步的前提
前提:同步中必须有多个线程并且使用同一个锁。
若将13-8中的代码Objectobj = new Object();放在while(true)的上面,这样每开启一次线程,每个线程的run都会给自己的线程创建一个锁,这样四个锁互不影响,就不能实现四个线程的同步了。
13-11,多线程-同步函数
用一个例子来说明代码中哪里存在安全隐患。
/*需求:有两个储户,去银行存钱,每人每次存100,共存三次。银行有金库,用sum来记录存入的钱数,两个人在同一个银行存,存的钱数都累加到sum中。*/class Bank {private int sum;/*这里可能出现安全隐患,在执行完sum = sum + num后,若CPU切走,输出可能出现问题*/public synchronized void add(int num) { //这就是同步函数,给函数加上synchronized修饰sum = sum + num;try {Thread.sleep(10);}catch(InterruptedException e) {}System.out.println("sum = " + sum);}}class Cus implements Runnable {//创建Bank对象,调用add方法,不能在run中创建,不然就相当于两个人分别去两个不同的银行存钱。private Bank b = new Bank();public void run() {for(int x=0;x<3;x++) {b.add(100); //只有一条语句,这里一般不出现问题}}}class BankDemo {public static void main(String[] args) {Cus c = new Cus();Thread t1 = new Thread(c);Thread t2 = new Thread(c);t1.start();t2.start();}}
13-12,多线程-验证同步函数的锁
1,以程序来说明,本例中一个线程用同步代码块,另一个用同步函数,若两个线程使用同一个锁,则结果正确,否则结果错误。
class Ticket implements Runnable {private int num = 100;private Object obj = new Object();//定义标记,若为true执行同步代码块,如果false执行同步函数boolean flag = true;public void run() {if(flag) {while(true) {synchronized(obj) { //这里如果改为this则结果正确if(num > 0) {try {Thread.sleep(10);}catch(InterruptedException e) {}System.out.println(Thread.currentThread().getName ()+"obj"+num--);}}}} else {while(true) {this.show(); //调用同步函数}}}public synchronized void show() {if(num > 0) {try {Thread.sleep(10);}catch(InterruptedException e) {}System.out.println(Thread.currentThread().getName()+"show"+num--);}}}class SynFunctionLockDemo {public static void main(String[] args) {Ticket t = new Ticket();Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();//让线程1冻结一下,不然CPU执行太快会直接按false执行下面的线程2try {Thread.sleep(10);}catch(InterruptedException e) {}t.flag = false;t2.start();}}
该程序说明了同步代码块和同步函数使用的不是同一个锁。
2,同步函数使用的锁是this。
同步函数和同步代码块的区别:
同步函数的锁是固定的this。
同步代码块的锁是任意的对象。
建议使用同步代码块,同步函数是同步代码块的简写,简写会方便一些,但是有弊端。
13-13,验证静态同步函数的锁
1,静态的同步函数使用的锁是:该函数所属字节码文件对象。
可以通过getClass()方式获取,也可以使用 当前类名.class 获取。
2,若把13-12中的代码的publicsynchronizedvoid show()改为public static synchronizedvoid show()则此方法为静态的,静态函数中没有this,其实静态方法随着类的加载而被加载进静态方法区后,也带有一个对象这个对象就是该方法所属类的字节码文件,所以在同步代码块的对象的位置上写上this.getClass()或Ticket.class就可以使结果正确。
13-14,单例模式涉及的多线程问题
饿汉式:
class Single {private static final Single s = new Single();private Single(){}public static Single getInstance() {return s; //操作共享数据的只有一句话,不存在安全问题}}
懒汉式:
13-15,多线程-死锁示例
死锁,常见的情况之一:同步的嵌套,以代码说明:
class Test implements Runnable {private boolean flag;Test(boolean flag) {this.flag = flag;}public void run() {if(flag) {while(true) {//这里为嵌套synchronized(MyLock.locka) {System.out.println(Thread.currentThread().getName()+"if locka");synchronized(MyLock.lockb) {System.out.println(Thread.currentThread().getName()+"if lockb");}}}} else {while(true) {//这里是嵌套synchronized(MyLock.lockb) {System.out.println(Thread.currentThread().getName()+"else lockb");synchronized(MyLock.locka) {System.out.println(Thread.currentThread().getName()+"else locka");}}}}}}class MyLock {//创建锁对象,静态对象直接类名调用public static final Object locka = new Object();public static final Object lockb = new Object();}class DeadLockTest {public static void main(String[] args) {//new两个Test对象,为了让t1进程执行if中的语句,让t2进程执行else中的语句。Test a = new Test(true);Test b = new Test(false);Thread t1 = new Thread(a);Thread t2 = new Thread(b);t1.start();t2.start();}}
说明,可能会出现的情况:
对象a初始化为true,所以线程t1执行if中的语句,这时线程t1持有了locka锁,并输出了if locka,这时CPU切走线程,线程t2开始执行,因为b的初始化为false,所以t2执行else中的语句,这时t2持有了lockb锁,并输出了elselockb,这时如果t2继续向下执行,将会索取locka锁,而这时locka锁正被t1拿着,从而无法获得,CPU转而执行t1,t1继续执行将会索取lockb锁,而lockb锁正被t2拿着,这样t1和t2都无法继续执行,就挂在这里,就行成了死锁。
若t2切走去执行t1,t1将索取lockb,这时lockb被t2拿着无法索取,CPU再执行t2,t2将索取locka,但locka被t1拿着,t1和t2都无法继续执行,也形成了死锁。
- Java基础13--多线程
- Java基础13:多线程编程
- Java基础/Java多线程
- Java基础-多线程基础篇
- java多线程基础
- Java多线程编程基础
- java多线程开发基础
- Java多线程基础
- Java -- 多线程技术基础
- 【java】多线程基础
- Java基础:多线程
- Java语言基础:多线程
- Java语言基础:多线程
- Java语言基础:多线程
- java多线程基础分析
- Java多线程编程基础
- java 多线程基础
- Java基础_多线程
- Table边框使用总结 ,只显示你要显示的边框
- Android实现开机自动运行程序
- finish 和 onDestroy 的区域
- 消除点击超链接 按钮时出现的虚线
- Win8激活码
- Java基础13--多线程
- postgresql问题总结
- 主机备份和远程备份同时进行
- 生育 生殖 長壽의 三情은 萬物의 本能 (韓長庚 易學原理總論)
- Tc wintc turbo c 写的有关图形的小程序 tc图形程序 带按键处理
- C# Winform清空界面控件值小技巧
- html框架frame
- JQuery教程自学笔记
- 两个场景切换