黑马程序员——基础知识总结_多线程
来源:互联网 发布:人工智能的编程语言 编辑:程序博客网 时间:2024/05/21 08:41
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——–
从进程讲起:
进程是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径或者叫一个控制单元。线程就是进程中的一个独立控制单元,线程在控制着进程的执行,一个进程至少有一个线程。单核cpu在某个时刻只能执行一个程序,我们看到的多个程序在同时启动,是因为cpu在以我们看不见的速度在各个进程间切换。多线程并发就是一个程序里面同时向cpu申请执行权,cpu执行谁,谁就运行。这个过程是cpu说了算的。我们无法预测。
图片来自于:http://www.open-open.com/lib/view/open1371818439463.html
第一话:创建线程的2种方法
第一种方法继承Thread类:
步骤:
1.定义类继承Thread类。
2.重写run方法。
3.调用线程的start方法启动线程接着执行run方法。
package Thread;//继承Thread类public class Thread_blogDemo extends Thread{ static int num = 0; Thread_blogDemo(String name) { super(name);//调用父类构造函数 } //重写run方法 public void run() { System.out.println(this.getName()+"---"+num++);// getName()获取线程的名字 } public static void main(String[] args) { Thread_blogDemo t1 = new Thread_blogDemo("t1Run"); Thread_blogDemo t2 = new Thread_blogDemo("t2Run"); Thread_blogDemo t3 = new Thread_blogDemo("t3Run"); //调用start方法开始线程 t1.start(); t2.start(); t3.start(); }}
结果图,2次结果不一样。可以看出cpu调用3个线程没跟着顺序来,这是多线程的表现。
第一种情况:
第二种情况:
start()方法是用来启动线程然后才调用run()方法,如果直接调用类中的run()方法,则只能算是单线程。不管运行多少次,都是一样的结果。
局限性:
第二种方法实现Runnable接口:
步骤:
1.实现Runnable接口
2.覆盖Runnable接口的run方法
3.通过Thread类建立线程对象。
4.将Ruannable接口的子类对象作为实际参数传递给Thread类的构造方法
5.调用Thread类的start方法,开启线程并调用Runnable接口子类的run方法
package Thread;class person{}public class Thread_blogDemo2 extends person implements Runnable //继承了person{ String name = null; public Thread_blogDemo2(String name) { this.name = name; } //重写Runnable中的run方法 public void run() { System.out.println(name); } public static void main(String[] args) { Thread_blogDemo2 t1 = new Thread_blogDemo2("Thread1"); Thread_blogDemo2 t2 = new Thread_blogDemo2("Thread2"); Thread_blogDemo2 t3 = new Thread_blogDemo2("Thread3");//调用Thread的start方法启动线程,并执行Runnable子类中的run方法 new Thread(t1).start(); new Thread(t2).start(); new Thread(t3).start(); }}
两种方法的区别:
1.继承Thread方法,使得该类无法继承其他类,不利于今后程序的扩展。而实现Runnable接口的方法解决了这一局限性。
2.继承方式的多线程执行代码放在Thread的子类中,而实现方法则是将多线程执行代码放在Runnable子类中。
第二话:多线程同步隐患
问题原因:
当多条语句在操作同一线程时,一个线程还没执行完该线程的所有代码,另外线程参与进来。
/*一个卖票的程序因为同步问题,导致了票数出现了负值*/class Ticket implements Runnable{ private int tick = 100; //线程run方法 public void run() { while(true) { { if(tick>0) { //Thread.sleep(30)时线程睡眠30ms为了让问题更容易暴露出来 try{Thread.sleep(30);}catch(Exception e){} //每个线程都有默认的名字,currentThread方法就是得出当前线程名称 System.out.println(Thread.currentThread().getName()+"sale..." +tick--); } } } } } class TicketDemoRunnable { public static void main(String[] args) { Ticket tic = new Ticket(); Thread t1 = new Thread(tic); Thread t2 = new Thread(tic); Thread t3 = new Thread(tic); Thread t4 = new Thread(tic); t1.start();//执行线程 t2.start(); t3.start(); t4.start(); } }
结果图:
锁的概念:
当一个方法或者语句块持有锁,那么当一个线程在操作该区域的时候其他线程即使获得cpu执行权也无法进入该区域,直到区域运行完代码释放了锁。要保证锁能起到作用,必须保证多个线程用到的是同一个锁
同步问题解决方法:
保证一个线程可以执行完该线程的多条语句,其他线程不能参与。也就是给在相应位置加锁。对此java提出了解决方案——同步代码块,和同步函数。
同步代码块:
class Ticket implements Runnable { private int tick = 100; //要保证是同一个锁,而锁可以是任意对象因此用Objec作为锁的对象 Object obj = new Object(); public void run() { //同步代码块 synchronized (obj) { while (true) { if (tick > 0) { try { Thread.sleep(30); } catch (Exception e) { } System.out.println(Thread.currentThread().getName() + "sale..." + tick--); } else return; } } }}
结果图:
同步函数:
在函数的返回值之前访问修饰符之后加上一个 synchronized ,这样就能给函数上锁了。同步函数的锁对象是什么呢?很容易想到this可以作为同步函数锁。synchronized void method(this
)
但是当函数方法静态的时候,问题出现了,静态不属于任何对象。于是又想到静态进内存时,内存中没有对象但是一定有该类对应的字节码文件对象。类名.class 该对象类型是Class。因此静态函数同步锁可以用 类名.class来保证锁是同一个。synchronized void method(类名.class)
死锁:
死锁的出现是由于同步中包含同步,同步函数含有同步代码块,题目交替持有锁,在某种情况下,两个程序各持有对方所需要的
锁。
package Thread;class Test implements Runnable{ private boolean flag; Test(boolean flag) { this.flag = flag; } public void run() { if(flag) { //a锁 synchronized(MyLock.locka) { System.out.println("if locka"); //b锁 synchronized(MyLock.lockb) { System.out.println("if lockb"); } } } else { //b锁 synchronized(MyLock.lockb) { System.out.println("else lockb"); //a锁 synchronized(MyLock.locka) { System.out.println("else locka"); } } } }}class MyLock{ static Object locka = new Object(); static Object lockb = new Object();}public class sisup { public static void main(String[] args) { //2个对象 Test test = new Test(false); Test test2 = new Test(true); //2个多线程 Thread t1 = new Thread(test); Thread t2 = new Thread(test2); //线程开始 t1.start(); t2.start(); }}
分析:在某一时刻t1拿到a锁,t2拿到了b锁。此时a请求的是b锁,b请求的是a锁,由于他们还没执行完被加锁的区域,于是锁一直没放。就这样t1拿不到b锁也没有满足释放锁的条件,同样的t2也拿不到a锁,也无法释放a锁。这就出现了死锁。
结果图:
- 黑马程序员——基础知识总结_多线程
- 黑马程序员—多线程基础知识
- 黑马程序员——基础知识总结_接口,多态
- 黑马程序员——基础知识总结_内部类
- 黑马程序员——基础知识总结_泛型
- 黑马程序员——基础知识总结_集合框架1
- 黑马程序员——基础知识总结_反射
- 黑马程序员——基础知识总结_网络编程
- 黑马程序员_多线程总结
- 黑马程序员_多线程总结
- 黑马程序员_多线程总结
- 黑马程序员——基础知识--多线程
- 黑马程序员——多线程的基础知识
- 黑马程序员—java基础_多线程
- 黑马程序员 Java基础知识总结-多线程
- 黑马程序员_JavaSE基础知识总结十四:多线程
- 黑马程序员_多线程的总结
- 黑马程序员_多线程学习总结(四)
- 工作周报047
- 优先队列详解
- python yield keyword
- 批处理命令学习
- Oracle数据库之表空间
- 黑马程序员——基础知识总结_多线程
- TCP三次握手和四次挥手协议
- My Upload.com product submission has been approved
- 最新全功能DNV sesam suite 2013 Full 1CD包含新的 DeepC 、GeniE和HydroD模块.
- JS中&&和||用法技巧
- kernel中路由表的实现
- java.sql 接口 PreparedStatement ResultSet DriverManager
- hdu 5269 ZYB loves Xor I && BestCoder Round #44
- BITBUCKET新手筆記