线程的几种运行状态
CPU执行资格:可以被cpu处理,在处理队列中排队。 CPU的执行权:正在被cpu处理。 冻结状态下,是释放执行资格和执行权的。 sleep是指定休眠的时间,到时自动唤醒。 wait()是需要notify()函数来唤醒的。
第一种方式具有局限性,如果必须继承Thread类来实现多线程,如果该类有父类,并且需要多线程执行呢? 此时应该如何解决这个问题呢?
此时可以实现Runnable接口来实现多线程。 Runable接口中只有一个方法,run方法。Thread的构造方法中,有可以传递实现Runnable接口的对象.
package lesson11; public class test4 { public static void main(String []args){ Demo1 a = new Demo1(); Thread a1 = new Thread(a); Thread b1 = new Thread(a); a1.start(); b1.start(); } } class Demo1 extends fu implements Runnable{ @Override public void run() { show(); } void show(){ for(int i=0;i<10;i++){ System.out.println(i+"----"+Thread.currentThread().getName()); } } } class fu{ } <<< 0----Thread-1 0----Thread-0 1----Thread-1 1----Thread-0 2----Thread-1 2----Thread-0 3----Thread-1 3----Thread-0 4----Thread-1 4----Thread-0 5----Thread-0 6----Thread-0 7----Thread-0 5----Thread-1 8----Thread-0 6----Thread-1 9----Thread-0 7----Thread-1 8----Thread-1 9----Thread-1创建线程的第二种方式:
1. 定义类实现Runnable接口。 2. 覆盖接口中的run方法,将线程的任务代码封装到run方法中。 3. 通过Thread类创建线程对象,并将Runnable接口的子类对象作为构造函数的参数进行传递。 4. 调用线程对象的start方法开启线程。第二种方法的好处
1. 将线程的任务从线程的子类中分离出来,进行了单独的封装。按照面向对象的思想将任务封装成对象。 2. 避免了java的单继承局限性。所以创建线程的第二种方式较为常用。
售票的例子
package lesson11; public class test5 { public static void main(String [] args){ Ticket a = new Ticket(); Thread a1 = new Thread(a); Thread b1 = new Thread(a); Thread c1 = new Thread(a); Thread d1 = new Thread(a); a1.start(); b1.start(); c1.start(); d1.start(); } } class Ticket implements Runnable{ private int ticketNum = 50; @Override public void run() { while (true){ if(ticketNum>0){ System.out.println(Thread.currentThread().getName()+"----"+ticketNum--); } } } } <<< Thread-0----50 Thread-3----47 Thread-2----48 Thread-1----49 Thread-2----44 Thread-3----45 Thread-0----46 Thread-3----41 Thread-2----42 Thread-1----43 Thread-2----38 Thread-3----39 Thread-0----40 Thread-3----35 Thread-2----36 Thread-1----37 Thread-2----32 Thread-3----33 Thread-0----34 Thread-3----29 Thread-2----30 Thread-1----31 Thread-2----26 Thread-3----27 Thread-0----28 Thread-3----23 Thread-2----24 Thread-1----25 Thread-2----20 Thread-3----21 Thread-0----22 Thread-3----17 Thread-3----15 Thread-2----18 Thread-1----19 Thread-2----13 Thread-3----14 Thread-0----16 Thread-3----10 Thread-2----11 Thread-1----12 Thread-2----7 Thread-3----8 Thread-0----9 Thread-3----4 Thread-2----5 Thread-1----6 Thread-2----1 Thread-3----2 Thread-0----3 package lesson11; public class test5 { public static void main(String [] args){ Ticket a = new Ticket(); Thread a1 = new Thread(a); Thread b1 = new Thread(a); Thread c1 = new Thread(a); Thread d1 = new Thread(a); a1.start(); b1.start(); c1.start(); d1.start(); } } class Ticket implements Runnable{ private int ticketNum = 50; @Override public void run() { while (true){ if(ticketNum>0){ try { Thread.sleep(10);//会抛出异常 System.out.println(Thread.currentThread().getName()+"----"+ticketNum--); } catch (InterruptedException e) { e.printStackTrace(); } } } } } <<< Thread-0----50 Thread-2----48 Thread-1----49 Thread-3----47 Thread-3----46 Thread-0----44 Thread-1----46 Thread-2----45 Thread-0----43 Thread-2----43 Thread-1----42 Thread-3----43 Thread-1----40 Thread-3----41 Thread-2----41 Thread-0----41 Thread-0----39 Thread-1----39 Thread-2----38 Thread-3----39 Thread-2----37 Thread-0----36 Thread-1----36 Thread-3----35 Thread-0----34 Thread-1----31 Thread-2----32 Thread-3----33 Thread-3----30 Thread-0----29 Thread-1----28 Thread-2----29 Thread-2----27 Thread-3----25 Thread-0----27 Thread-1----26 Thread-0----24 Thread-3----23 Thread-1----22 Thread-2----21 Thread-3----20 Thread-0----19 Thread-1----18 Thread-2----17 Thread-3----16 Thread-0----16 Thread-2----15 Thread-1----14 Thread-3----13 Thread-0----13 Thread-2----12 Thread-1----12 Thread-3----11 Thread-0----11 Thread-2----10 Thread-1----9 Thread-3----8 Thread-0----8 Thread-1----7 Thread-2----6 Thread-0----5 Thread-3----5 Thread-1----4 Thread-2----3 Thread-3----2 Thread-0----2 Thread-1----1 Thread-2----0 Thread-3-----1 Thread-0-----1这个方法有异常抛出,而覆盖的run方法中没有异常抛出的声明,因此只能再此捕捉。
此时观察结果,发现有不对的地方,这就是线程的安全问题。
产生的原因:
1. 多个线程在操作共享的数据。 2. 操作共享数据的线程代码有多条(比如本例中判断加数据是两条语句)解决方法:
将多条操作共享的数据代码封装起来,当有线程在执行这些代码时,其他线程不可以参与运算。 必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。 在java中使用同步代码块就能解决。 sybchronized(对象){ 代码 } package lesson11; public class test5 { public static void main(String [] args){ Ticket a = new Ticket(); Thread a1 = new Thread(a); Thread b1 = new Thread(a); Thread c1 = new Thread(a); Thread d1 = new Thread(a); a1.start(); b1.start(); c1.start(); d1.start(); } } class Ticket implements Runnable{ private int ticketNum = 50; Object obj = new Object(); @Override public void run() { while (true){ synchronized (obj){ if(ticketNum>0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"----"+ticketNum--); } } } } } <<< Thread-0----50 Thread-0----49 Thread-0----48 Thread-0----47 Thread-0----46 Thread-0----45 Thread-0----44 Thread-0----43 Thread-0----42 Thread-0----41 Thread-0----40 Thread-0----39 Thread-0----38 Thread-0----37 Thread-0----36 Thread-0----35 Thread-0----34 Thread-0----33 Thread-0----32 Thread-0----31 Thread-0----30 Thread-0----29 Thread-0----28 Thread-0----27 Thread-0----26 Thread-0----25 Thread-0----24 Thread-0----23 Thread-0----22 Thread-0----21 Thread-0----20 Thread-0----19 Thread-0----18 Thread-0----17 Thread-0----16 Thread-0----15 Thread-0----14 Thread-0----13 Thread-0----12 Thread-0----11 Thread-0----10 Thread-0----9 Thread-0----8 Thread-0----7 Thread-0----6 Thread-0----5 Thread-0----4 Thread-0----3 Thread-0----2 Thread-0----1啥情况?怎么变成一个线程了,其实是休眠的时间太长了,缩短时间或者是增加Ticketnum就能看到其他线程
package lesson11; public class test5 { public static void main(String [] args){ Ticket a = new Ticket(); Thread a1 = new Thread(a); Thread b1 = new Thread(a); Thread c1 = new Thread(a); Thread d1 = new Thread(a); a1.start(); b1.start(); c1.start(); d1.start(); } } class Ticket implements Runnable{ private int ticketNum = 50; Object obj = new Object(); @Override public void run() { while (true){ synchronized (obj){ if(ticketNum>0){ try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"----"+ticketNum--); } } } } } <<< Thread-0----50 Thread-0----49 Thread-3----48 Thread-3----47 Thread-3----46 Thread-3----45 Thread-3----44 Thread-3----43 Thread-3----42 Thread-3----41 Thread-3----40 Thread-3----39 Thread-3----38 Thread-3----37 Thread-3----36 Thread-3----35 Thread-3----34 Thread-3----33 Thread-3----32 Thread-3----31 Thread-3----30 Thread-3----29 Thread-3----28 Thread-3----27 Thread-3----26 Thread-3----25 Thread-3----24 Thread-3----23 Thread-3----22 Thread-3----21 Thread-3----20 Thread-3----19 Thread-3----18 Thread-3----17 Thread-3----16 Thread-3----15 Thread-3----14 Thread-3----13 Thread-3----12 Thread-3----11 Thread-3----10 Thread-3----9 Thread-3----8 Thread-3----7 Thread-3----6 Thread-3----5 Thread-3----4 Thread-3----3 Thread-3----2 Thread-3----1底层实现为线程同步锁,obj就是那个锁(简单理解)。 同步的好处:
1. 同步解决了线程的安全问题弊端:
1。 降低了效率同步的前提:同步中使用同一个锁。
package lesson11; public class test5 { public static void main(String [] args){ Ticket a = new Ticket(); Thread a1 = new Thread(a); Thread b1 = new Thread(a); Thread c1 = new Thread(a); Thread d1 = new Thread(a); a1.start(); b1.start(); c1.start(); d1.start(); } } class Ticket implements Runnable{ private int ticketNum = 50; @Override public void run() { Object obj = new Object(); while (true){ synchronized (obj){ if(ticketNum>0){ try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"----"+ticketNum--); } } } } } <<< Thread-0----50 Thread-2----47 Thread-1----48 Thread-3----49 Thread-2----45 Thread-1----44 Thread-0----43 Thread-3----46 Thread-2----42 Thread-1----39 Thread-0----41 Thread-3----40 Thread-3----38 Thread-0----38 Thread-2----38 Thread-1----37 Thread-3----36 Thread-0----36 Thread-2----36 Thread-1----36 Thread-2----35 Thread-3----34 Thread-0----35 Thread-1----33 Thread-3----32 Thread-2----31 Thread-0----30 Thread-1----29 Thread-2----28 Thread-1----26 Thread-0----28 Thread-3----27 Thread-3----25 Thread-0----22 Thread-1----23 Thread-2----24 Thread-0----21 Thread-3----21 Thread-2----21 Thread-1----20 Thread-0----19 Thread-1----16 Thread-2----17 Thread-3----18 Thread-1----15 Thread-3----13 Thread-0----15 Thread-2----14 Thread-2----12 Thread-0----10 Thread-3----11 Thread-1----12 Thread-0----9 Thread-1----9 Thread-3----9 Thread-2----8 Thread-0----7 Thread-1----7 Thread-3----6 Thread-2----7 Thread-1----5 Thread-0----5 Thread-3----5 Thread-2----5 Thread-0----4 Thread-3----4 Thread-1----4 Thread-2----3 Thread-1----2 Thread-3----1 Thread-2----0 Thread-0----2为什么又出现问题了呢?因为四个线程用的不是同一个锁。
package lesson11; public class test6 { public static void main(String [] args){ Cus c = new Cus(); Thread a1 = new Thread(c); Thread b1 = new Thread(c); a1.start(); b1.start(); } } class Bank{ private int sum; public void add(int num){ sum +=num; System.out.println("sum="+sum); } } class Cus implements Runnable{ Bank b = new Bank(); @Override public void run() { for(int x =0;x<3;x++){ b.add(100); } } } <<< sum=200 sum=300 sum=400 sum=200 sum=500 sum=600是否有线程安全隐患? 有,在sum+=num阶段有可能会有隐患发生。
package lesson11; public class test6 { public static void main(String [] args){ Cus c = new Cus(); Thread a1 = new Thread(c); Thread b1 = new Thread(c); a1.start(); b1.start(); } } class Bank{ private int sum; private Object obj = new Object(); public void add(int num){ synchronized (obj){ sum +=num; System.out.println("sum="+sum); } } } class Cus implements Runnable{ Bank b = new Bank(); @Override public void run() { for(int x =0;x<3;x++){ b.add(100); } } } <<< sum=100 sum=200 sum=300 sum=400 sum=500 sum=600同步代码块是一种封装,函数也是对功能的一种封装。
package lesson11; public class test6 { public static void main(String [] args){ Cus c = new Cus(); Thread a1 = new Thread(c); Thread b1 = new Thread(c); a1.start(); b1.start(); } } class Bank{ private int sum; private Object obj = new Object(); public synchronized void add(int num){//注意这里,这个函数就变成了同步函数 sum +=num; System.out.println("sum="+sum); } } class Cus implements Runnable{ Bank b = new Bank(); @Override public void run() { for(int x =0;x<3;x++){ b.add(100); } } } <<< sum=100 sum=200 sum=300 sum=400 sum=500 sum=600 package lesson11; public class test5 { public static void main(String [] args){ Ticket a = new Ticket(); Thread a1 = new Thread(a); Thread b1 = new Thread(a); Thread c1 = new Thread(a); Thread d1 = new Thread(a); a1.start(); b1.start(); c1.start(); d1.start(); } } class Ticket implements Runnable{ private int ticketNum = 50; @Override public synchronized void run() { Object obj = new Object(); while (true){ if(ticketNum>0){ try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"----"+ticketNum--); } } } } <<< Thread-0----50 Thread-0----49 Thread-0----48 Thread-0----47 Thread-0----46 Thread-0----45 Thread-0----44 Thread-0----43 Thread-0----42 Thread-0----41 Thread-0----40 Thread-0----39 Thread-0----38 Thread-0----37 Thread-0----36 Thread-0----35 Thread-0----34 Thread-0----33 Thread-0----32 Thread-0----31 Thread-0----30 Thread-0----29 Thread-0----28 Thread-0----27 Thread-0----26 Thread-0----25 Thread-0----24 Thread-0----23 Thread-0----22 Thread-0----21 Thread-0----20 Thread-0----19 Thread-0----18 Thread-0----17 Thread-0----16 Thread-0----15 Thread-0----14 Thread-0----13 Thread-0----12 Thread-0----11 Thread-0----10 Thread-0----9 Thread-0----8 Thread-0----7 Thread-0----6 Thread-0----5 Thread-0----4 Thread-0----3 Thread-0----2 Thread-0----1为什么一直是线程0呢? 因为整个函数被同步了,当0线程只要执行个函数,就获得了CPU执行权,而其他线程是有cpu执行资格,而内部是while(True),因此线程0不会释放执行权。 这种问题应该怎么解决呢?
package lesson11; public class test5 { public static void main(String[] args) { Ticket a = new Ticket(); Thread a1 = new Thread(a); Thread b1 = new Thread(a); Thread c1 = new Thread(a); Thread d1 = new Thread(a); a1.start(); b1.start(); c1.start(); d1.start(); } } class Ticket implements Runnable { private int ticketNum = 50; @Override public void run() { Object obj = new Object(); while (true) { fun(); } } public synchronized void fun(){ if (ticketNum > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "----" + ticketNum--); } } } <<< Thread-0----50 Thread-0----49 Thread-0----48 Thread-0----47 Thread-0----46 Thread-0----45 Thread-0----44 Thread-0----43 Thread-0----42 Thread-0----41 Thread-3----40 Thread-3----39 Thread-3----38 Thread-3----37 Thread-3----36 Thread-3----35 Thread-3----34 Thread-3----33 Thread-3----32 Thread-3----31 Thread-3----30 Thread-3----29 Thread-3----28 Thread-3----27 Thread-3----26 Thread-3----25 Thread-3----24 Thread-3----23 Thread-3----22 Thread-3----21 Thread-3----20 Thread-3----19 Thread-3----18 Thread-3----17 Thread-3----16 Thread-3----15 Thread-2----14 Thread-2----13 Thread-2----12 Thread-2----11 Thread-2----10 Thread-2----9 Thread-2----8 Thread-2----7 Thread-2----6 Thread-2----5 Thread-2----4 Thread-2----3 Thread-2----2 Thread-2----1此时将内部需要同步的代码封装成一个函数即可。 同步函数的锁是this(调用函数的this)
同步代码块是任意的对象。
建议使用同步代码块
静态的同步函数使用的是 该函数所属的字节码文件对象 可以用getclass方法获取,也可以用当前类名.class形式表示。
package lesson11; public class test7 { public static void main(String [] args){ } } //饿汉式 class Single{ private static final Single s = new Single(); private Single(){}; public static Single getInstance(){ return s; } } //懒加载式 class SingleA{ private static SingleA a = null; public static SingleA getInstance(){ if(a==null){ a = new SingleA();//多线程在此处会不会出现问题 } return a; } }当一个线程执行到a = new SingleA();时,失去执行权,另一个线程判断a是否为null,也进入判断体内。此时不能保证单例模式唯一性了。
如何解决以上的问题?
class SingleA{ private static SingleA a = null; public static synchronized SingleA getInstance(){ if(a==null){ a = new SingleA();//多线程在此处会不会出现问题 } return a; } }将函数加上synchronized 修饰符,有一个线程执行该方法时,其他线程就无法执行,可以解决以上的问题。但是降低了效率,每一次获取对象都要判断一下有没有锁。
class SingleA{ private static SingleA a = null; public static SingleA getInstance(){ if(a==null){ synchronized (SingleA.class){ if(a==null){ a = new SingleA();//多线程在此处会不会出现问题 } } } return a; } }以上为改进效率后的代码,当SingleA 对象不为空时就直接返回单例对象,而不再进行锁的判断。
相比较懒加载,饿汉式多线程不会发生类似的问题。
发生死锁了,因为一个要获取obj锁,一个要获取this锁结果都获取不到对方的锁。就发生了死锁的情况。
package lesson11; import java.util.concurrent.ThreadLocalRandom; public class test8 { public static void main(String [] args){ Test a = new Test(true); Test b = new Test(false); Thread c = new Thread(a); Thread d = new Thread(b); c.start(); d.start(); } } class Test implements Runnable{ private boolean flag; Test(boolean flag){ this.flag = flag; } public void run(){ if(flag){ synchronized (Mylock.locka){ System.out.println(Thread.currentThread().getName()+"locka----"); synchronized (Mylock.lockb){ System.out.println(Thread.currentThread().getName()+"lockb----"); } } } else{ synchronized (Mylock.lockb){ System.out.println(Thread.currentThread().getName()+"lockb----"); synchronized (Mylock.locka){ System.out.println(Thread.currentThread().getName()+"locka----"); } } } } } class Mylock{ public static final Object locka = new Object(); public static final Object lockb = new Object(); } <<< Thread-0locka---- Thread-1lockb----如果死锁不一定是一个对象内发生的。