多线程--第一次小结
提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档文章目录一、线程和进程的区别和共同点二、创建线程1.继承Thread,重写run方法2.实现Runnable接口,重写run3.继承Thread,重写run,使用匿名内部类4.使用匿名内部类,基于Runnable5.lambda写法(简化)三.线程安全线程安全问题产生的原因用synchronized上锁来解决线程安全问题一、线程和进程的区别和共同点1.进程中包含线程一个进程中可以有一个线程,也可以有多个线程但是不能有0个线程.2.进程是资源分配的基本单位,线程是调度执行的基本单位3.每个进程都有独立资源,一个进程的多个线程,共用一份资源4.进程与进程之间是隔离的,同一个进程中的线程是共享资源的好处是:开辟线程和销毁线程的开销就很小坏处是:容易发生冲突(一个简单的比喻,线程多了就有些线程吃不上肉了,从而罢工).二、创建线程1.继承Thread,重写run方法classMyThreadextendsThread{publicvoidrun(){System.out.println(hello thread);}}publicclassDemo1{publicstaticvoidmain(String[]args){MyThreadtnewMyThread();t.start();}}这里用的是继承的方法来书写,但是这样写升高了耦合度,不太推荐.2.实现Runnable接口,重写runclassMyThreadimplementsRunnable{publicvoidrun(){System.out.println(hello thread);}}publicclassdemo2{publicstaticvoidmain(String[]args){RunnablernewMyThread();ThreadtnewThread(r);t.start();}}这里是利用接口来规则了MyThread,降低了耦合性,在日后的工程里也更加友好3.继承Thread,重写run,使用匿名内部类匿名和具名的区别,因为我们写代码的时候,有的时候我们只用一次就丢掉了,这样我们设置一个匿名的性价比就非常的高.publicclassdemo3{publicstaticvoidmain(String[]args){ThreadtnewThread(){publicvoidrun(){System.out.println(hello thread);}};t.start();}}4.使用匿名内部类,基于Runnablepublicclassdemo3{publicstaticvoidmain(String[]args){ThreadtnewThread(newRunnable(){Overridepublicvoidrun(){System.out.println(hello thread);}});t.start();}这里可以理解为Thread是一个写作业的人,而我们传进去的Runnable,这个为我们需要完成的任务,这个降低了耦合度…5.lambda写法(简化)publicclassdemo3{publicstaticvoidmain(String[]args){ThreadtnewThread(()-{System.out.println(hello Thread);});t.start();}}这种写法是最简单的写法,lambda表达式只能针对接口里只有一个方法的,他会直接帮你直接重写run方法.三.线程安全线程安全问题产生的原因1,[根本原因] 线程调度是随机的,一个线程执行到任意一个指令都有可能直接会被cpu调走(这是操作系统固定的)2.[直接原因] 针对变量的修改操作并不是原子性的3.多线程同时修改同一个变量4.内存可见性问题引起的线程安全问题.5.指令重新安排引起的线程安全问题.当然线程安全问题是一个概率问题.用synchronized上锁来解决线程安全问题首先我们先来看一bugpublicclassdemo3{publicstaticintcount;publicstaticvoidmain(String[]args)throwsInterruptedException{Threadt1newThread(()-{for(inti0;i50000;i){count;}});Threadt2newThread(()-{for(inti0;i50000;i){count;}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}}这里的运行结果显然和我们预想的不太一样,理论上来说应该输出的是100000这就是线程不安全的典型表现。在进行数据读取和运算时CPU 会与内存之间进行数据交互。以当前程序为例count 本质上分为三步load 读取、add 自增、save 写回。由于操作系统会随机调度两个线程交替执行指令这三步操作无法保证原子性可能在执行过程中被其他线程打断最终导致数据结果错误。所以我们就要为这些线程上个锁,上完锁之后就可以是这些原本并发执行的,变成串行执行.publicclassdemo3{publicstaticintcount;publicstaticvoidmain(String[]args)throwsInterruptedException{ObjectlockernewObject();Threadt1newThread(()-{for(inti0;i50000;i){synchronized(locker){count;}}});Threadt2newThread(()-{for(inti0;i50000;i){synchronized(locker){count;}}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}}**加了锁以后就变成了串行执行了,最后也得到了我们想要的结果了.