------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一,创建线程的两种方式:
创建线程的第一种方式:继承Thread类
步骤:1.定义类继承Thread
2.复写Thread类中的run()方法:将自定义的代码存储在run里让线程执行。
3.调用线程的start()方法,该方法有两个作用,启动线程,调用run方法。必须要调用,否则创建了线程但没有运行。
Thread类用于描述线程,该类定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法。
线程都有自己的默认名称,都是从0开始编号。
创建线程的第二种方法:声明实现Runnable接口的类,然后该类覆盖run方法。
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法::将线程要运行的代码存放在该方法中。
3.通过Thread类建立线程对象
4.将Runnable接口的子类对象作为实际参数传递给Thead类的构造函数。
5.调用Thread类的start方法,启动线程,并调用Runnable接口子类中的run方法
二:实现方式和继承方式的区别(非常重要) :
实现方式避免了多继承的局限性。Java是单继承的,继承了Thread类就无法继承其他类。
建议使用实现方式。(资源可以独立)
线程代码存放位置不同:继承方式存放在Thread子类run方法中,实现方式存放在接口子类run方法中。
三:安全问题:
在售票的例子中,出现0,-1,-2等错票。
原因:当多条语句操作一个共享数据时,一个线程对多条语句只执行了其中一部分,还没执行完另一个线程参与进来执行,导致共享数据出错。解决办法,让一个线程执行完,过程中,其他线程不可参与。
Java为了解决安全问题提供了专业的解决方式:同步代码块。
synchronized(对象){
需要被同步的代码块。
对象如同锁,持有锁的线程可以在同步中执行,就如同火车上的卫生间。
必须保证同步中只有一个线程在执行,但是,只将操作共享数据的代码放进同步、
}
如何找问题:
1.明确哪些代码是多线程运行代码
2.明确共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的。
四:死锁:同步中嵌套同步,并且锁不同。
例子:
- /*需求;死锁程序
- 线程1持有locka锁,进入同步代码块儿后继续执行代码想要持lockb锁,
- 同时线程2持有lockb锁,继续执行代码想要获取locka锁,
- 但是,两个线程都想获取对方持有的锁,但都不放手自己的锁,因此导致死锁。
- */
- class Test implements Runnable{
- private boolean flag;
- Test(boolean flag){
- this.flag = flag;
- }
- public void run(){
- if(flag){
- synchronized(Lock.locka){
- System.out.println("locka...");
- synchronized(Lock.lockb){
- System.out.println("lockb...");
- }
- }
- }
- else{
- synchronized(Lock.lockb){
- System.out.println("lockb...");
- synchronized(Lock.locka){
- System.out.println("locka...");
- }
- }
- }
- }
- }
- class Lock{
- static Object locka = new Object();
- static Object lockb = new Object();
- }
- class DeadLock{
- public static void main(String[] args){
- Thread t1 = new Thread(new Test(true));
- Thread t2 = new Thread(new Test(false));
- t1.start();
- t2.start();
- }
- }
五:线程间通信: 其实就是多个线程操作同一个资源,但是动作不同。
等待唤醒机制:
线程池:处于wait()状态的线程都在线程池中等待,按顺序存入,被叫醒时通常是第一个被叫醒。
wait():等待,
notify()/notifyAll() :唤醒等待的线程。
注意:wait(),notify()等方法被定义在Object中,必须用在同步中,同时,必须指持有锁的线程。因为要对持有监视器(锁)的线程操作。 x.wait();
只有同一个锁上的被等待线程可以被同一个锁上的notify唤醒。即等待和唤醒必须是同一把锁,但是,锁可以是任意对象,所以,可以被任意对象调用的方法定义在Object中。
JDK1.5版本中提供了多线程升级解决方案,将同步synchronized替换成显式的Lock操作。
将Object中的wait,notify,notifyAll,替换为Condition对象,该对象可以通过lock锁获取。
实现了本方只唤醒对方的操作。
六:如何停止线程:只有一种方法,就是run方法结束,开启多线程,运行代码多数是循环结构,只要控制住循环,就可以让run方法结束,线程就结束了。
特殊情况:当线程处于冻结状态,就无法读取结束标记,线程不会结束,当没有指定的方式让冻结的线程恢复到运行状态,这时需要对冻结状态
进行清除,强制让线程恢复到运行状态中,这样就可以操作标记,让线程结束。
Thread类中提供了该方法:interrupt();