class="p0" style="text-align: left; line-height: 15.75pt; margin-top: 0pt; margin-bottom: 0pt; background: #bda997;">----------------------?android开发、java培训、期待与您交流!?----------------------
1、进程和线程有什么区别?
进程:正在执行的程序,线程是进程中的内容。每个应用程序中至少有一个线程,每一个进程执行都有一个执行顺序,该顺序是一个执行路径或者叫一个控制单元。进程就是用于定义空间标示空间,进程以启动在内存中就分配一块空间。
线程:就是进程中的一个独立的控制单元线程在控制着进程的执行,一个进程中至少有一个线程。所以说进程中包含线程,而线程决定进程的生命周期。
?
2、多线程存在的意义以及特性
多线程意义:多代码同时运行,提高效率。
特性:随机性,在某一时刻只能有一个线程在运行(多核除外)比如我们用电脑即能听歌,有能看视频,因为CPU在做着高速的切换,以达到看上去同时运行的效果,我们可以形象的把多线程运行行为看做是在抢夺CPU的执行权,谁抢到谁执行。
什么时候使用线程:当某些代码需要被同时执行时就用单独的线程进行封装。
?
3、创建线程方法之一(继承Thread类)
//创建线程?通过对API文档的查找发现java已经封装好对这类事物操作的对象?即Thread类
//创建线程的第一种方法?继承Thread类?并复写其中的run方法
步骤:1、定义一个类继承Thread
???2、覆写Thread类中的run方法(将自定义的代码存储到在run方法中,让线程运行)
???3、调用线程中的start方法,该方法有两个作用:启动线程和调用线程中run方法
eg:代码示例
class?Demo?extends?Thread
{
public?void?run()
{
for(int?i=0;i<100;i++)
{
System.out.println("第一个线程"+"-----"+i);
}
}
}
class??ThreadDemo1
{
public?static?void?main(String[]?args)?
{
//建立好一个对象就创建好一个线程
Demo?d=?new?Demo();
//调用start方法?目的是开启线程并调用run方法
d.start();
for(int?i=0;i<100;i++)
{
System.out.println("主线程main"+"-----"+i);
}
}
}
//运行结果是互相交错的?因为该程序中有两个线程?main线程和d线程?cup在这两个线程之间做着高速切换的动作
//多个线程在抢cpu资源
?
4、?为什么要覆盖run方法?
Thread类中用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是说Thread类中的run方法,是用于存储线程要运行的代码。(就是告诉线程你如果要干活,就干这一块别的不要管就行)。
定义类声明实现Runnable接口,该类然后实现run方法,之后创建该类的实例,在创建Thread时将该类实例作为参数传递并启动线程。
步骤:1、定义类实现Runnable接口
???2、覆盖Runnable接口中的run方法(将线程要运行的代码存放在run方法中)
???3、通过Thread类建立线程对象
???4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
???5、调用Thread类的start()方法,开启线程并调用Runnable接口子类的run方法
小知识:
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为自定义的run方法所属对象时Runnable接口的子类对象,所以要让线程去执行制定对象的run方法,就必?须明确该run方法所属对象。
代码示例:
class?Ticket?implements?Runnable
{
private?int?ticket?=?100;
public?void?run()
{
while(true)
{
if(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"----"+?ticket--);
//Thread类中有一个静态方法currentThread()获取当前线程对象getName()线程名字
}
}
}
}
class??maiPiao
{
public?static?void?main(String[]?args)?
{
//创建Runnable子类对象
Ticket?t?=?new?Ticket();
//通过Thread类?创建线程对象并且把Runnable接口子类对象作为参数传递给Thread类
Thread?t1?=?new?Thread(t);
Thread?t2?=?new?Thread(t);
Thread?t3?=?new?Thread(t);
Thread?t4?=?new?Thread(t);
//通过Thread对象调用start方法?开启线程并执行Runnable子类对象中的run方法
t1.start();
t2.start();
t3.start();
t4.start();
?
}
}
?
7、定义线程继承和实现的区别?
1、实现方式好处避免了单继承的局限性(建议使用实现方式)
2、继承Thread类线程代码存放在Thread子类run方法中
3、实现Runnable线程代码存放在接口子类的run方法中
?
8、线程安全问题
8.1?问题怎么产生的
在多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一条线程就参与进来执行,导致共享数据的错误。就比如火车站买票,火车票是共享数据而多个窗口是线程都在同时操作火车票。
?
8.2?怎么解决问题
思路:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中其他线程不可以参与执行、
Java对多线程安全问题提供了专业的解决方式就是同步代码块
格式:
synchronized(锁对象)
{
需要被同步的代码(正在操作共享数据的代码);
}
对象:对象如同锁,持有锁的线程才可以在同步中执行(火车卫生间),没有持有锁的线程及时有CPU执行权也进不去,因为没有获取锁,锁就相当于一个通行证?只有持有通行证的线程才可以进去。
?
8.3?同步的前提
1、必须有两个或两个以上的线程
2、必须是多线程使用同一个锁
3、必须保证同步中只能有一个线程
?
8.4?同步的利与弊
利:解决了多线程的安全问题,保证数据的准确性
弊:多个线程都需要判断锁较为消耗资源
?
8.5?针对多线程如何找问题
1、明确哪些代码是多线程运行的代码
2、明确共享数据
3、明确多线程运行代码中哪些语句是操作共享数据的
?
??8.6?同步函数
就是把synchronized关键字作为修饰符放在函数上修饰,该函数就变成了同步函数。
eg:代码示例
public?synchronized?void?add()?
{
函数内部代码;
}
同步函数锁的知识点:同步函数需要被对象调用,那么函数都有一个所属对象的引用就是this,所以同步函数使用的锁是this。如果同步函数被静态所修饰后使用的锁是该类的字节码文件即:类名.class?因为静态进内存时内存中没有本类对象,但是一定有该类对应的字节码文件对象,所以被静态修饰的同步函数持有的锁是该类的字节码文件。
?
8.7单例设计模式(延迟加载?静态修饰同步函数?)
class?Single?
{
//私有构造函数
private?Single(){}
//建立本类对象引用
private?static?Single?s?=?null;
//提供公共访问方式?返回本类对象
public?static?Single?getSingle()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
{
s=?new?Single();
}
}
}
return?s;
}
}
?
8.8死锁
???什么是死锁?
????所谓死锁就是指两个以上的线程互相都有要求使用对方已经占有的资源而导致无法继续运
????行的现象。就是同步中嵌套同步。
???简单的一个死锁程序代码:
???public?class?DeadLock
???{
????private??static?final?Object?obj1?=?new?Object();
????private?static?final?Object??obj2?=?new?Object();
????public?class?Run1?implements?Runnable
????{
?????String?threadName?=?Thread.currentThread().getName();
?????public?void?run()
?????{
??????synchronized(obj1)
??????{?????
???????System.out.println(threadName+"进入同步块obj准备进入obj2");
???????synchronized(obj2)
???????{
????????System.out.println(threadName+"已经进入同步块obj2");
???????}
??????}
?????}
????}
????public?class?Run2?implements?Runnable
????{
?????String?threadName?=?Thread.currentThread().getName();
?????public?void?run()
?????{
??????synchronized(obj2)
??????{?????
???????System.out.println(threadName+"进入同步块obj准备进入obj1");
???????synchronized(obj1)
???????{
????????System.out.println(threadName+"已经进入同步块obj1");
???????}
??????}
?????}
??????
????}
????public?static?void?main(String[]?args)
????{
?????Thread?t1?=?new?Thread(new?DeadLock().new?Run1());
?????Thread?t2?=?new?Thread(new?DeadLock().new?Run2());
?????t1.start();
?????t2.start();
????}?
???}
?
9、线程之间的通讯
9.1?线程通讯概述
线程间通讯其实就是多个线程在操作同一资源,但操作的动作不同,例如两辆车一辆拉煤一辆送煤。当两个线程有安全问题是需要根据同步的前提进行判断(两个或两个以上的线程,锁必须相同,必须保证同步中只用一个线程在运行)
9.2?等待唤醒机制
wait(?)你等会、notify(?)你醒醒、notifyAll(?)都醒了唤醒线程池中所有的线程,这三个方法全用在同步中,因为要对持有监视器(锁)的线程进行操作,所以要使用在同步中,因为只有同步才具有锁。
?
9.3为什么操作线程的放大都定义在Object中呢?
因为这些方法在操作同步中线程时都必须要标示他们所操作线程持有的锁。只有同一个锁上被等待的线程可以被同一个锁上的notify()唤醒,不可以对不同锁中的线程进行唤醒。也就是说等待和唤醒机制必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
?
10、JDK1.5版本后线程升级新特性
在JDK1.5版本后,线程出现了新特性,在java.util.concurrent.locks包中的Lock类,将同步synchronized替换成现实的Lock操作,将Object中的wait(?)、notify(?)、notifyAll(?)替换成了Condition对象,该对象可以对Lock锁进行获取,在消费者生产者实例中,实现了只唤醒对方操作。
import?java.util.concurrent.locks.*;
class?chejian
{
private?String?name;
private?int?count=0;
private?boolean?flag=false;
Lock?lock?=?new?ReentrantLock();
Condition?condition_sheng?=?lock.newCondition();
Condition?condition_xiao?=?lock.newCondition();
public??void?set(String?name)throws?InterruptedException
{
lock.lock();//获取锁
try
{
while(flag)
condition_sheng.await();//线程等待
this.name?=?name;
count++;
System.out.println(name+"————生产者————"+count);
flag=true;
condition_xiao.signal();//唤醒线程
}
finally
{
lock.unlock();//释放锁
}
}
public??void?get()throws?InterruptedException
{
lock.lock();//获取锁
try
{
while(!flag)
condition_xiao.await();//线程等待
System.out.println(name+"----消费者-----"+count);
flag=false;
condition_sheng.signal();//唤醒线程
}
finally?
{
lock.unlock();//释放锁
}
}
}
//生产者线程
class?sheng?implements?Runnable
{
chejian?c;
sheng(chejian?c)
{
this.c?=?c;
}
public?void?run()
{
while?(true)
{
try
{
c.set("商品");
}
catch?(InterruptedException?e)
{
}
}
}
}
//消费线程
class?xiao?implements?Runnable
{
chejian?c;
xiao(chejian?c)
{
this.c?=?c;
}
public?void?run()
{
while?(true)
{
try
{
c.get();
}
catch?(InterruptedException?e)
{
}
}
}
}
class??oneFive
{
public?static?void?main(String[]?args)?
{
chejian?c?=?new?chejian();
xiao?x?=?new?xiao(c);
sheng?s?=new?sheng(c);
?
Thread?t1=?new?Thread(x);
Thread?t2=?new?Thread(x);
Thread?t3=?new?Thread(s);
Thread?t4=?new?Thread(s);
?
t1.start();
t2.start();
t3.start();
t4.start();
?
}
}
?
11、线程中的其他方法
线程停止原理:只有一种就是run方法结束,开启多线程运行,运行代码通常是循环结构,只要控制住循环就尅让run方法结束,也就是线程结束。
守护线程:setDaemon(boolean??b)?传入true将该线程标记为守护线程或用户线程,该方法应该在启动线程前调用,即在start()前调用之前。当正在运行的线程都是守护线程时,Java?虚拟机退出。
eg:t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
join(?):等待该线程终止(抢夺主线程的CPU执行权),例如当A线程执行到了B线程的join()方法时,A就会等待,等B线程都执行完A才会执行,join?()可以用于临时加入线程执行。
·
优先级设置:setPriority(int?newPriority)//线程优先级默认为5,优先级范围是1~10,即抢夺CPU执行的频率。
暂停线程:yield()?暂停当前正在执行的线程对象,并执行其他线程。
----------------------?android开发、java培训、期待与您交流!?----------------------
?
<!--EndFragment-->