----------------------------------android培训、java培训、期待与您交流!------------------------------
什么是进程:
当前正在运行的程序!
线程的概念:
进程在执行过程中,可能需要多个任务同时执行,每个任务的执行者就是线程。
线程就是进程的控制单元;
一个进程中至少有一个线程。
多线程的用途:
同时执行多个任务,可以提高效率。
可以完成多个任务并发的效果。
理解线程:
人:是执行任务的人,即线程。
任务:线程要执行的任务,这里说的任务,通常用一个方法表示。
JVM启动的时候会有一个进程java.exe,该进程至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程成为主线程。jvm启动的不止一个线程,还有负责垃圾回收机制的线程。
创建线程的方式有两种:
第一种,继承Thread类:
子类覆盖Thread类中的run方法,将线程运行的代码存放到run方法中。
建立子类对象的线程,调用start()方法。
public class SyncDemo {
public static void main(String[] args) throws InterruptedException{
Thread t=new SynThread();
t.start();//开启线程并执行该线程的run方法;
t.run();//仅仅是对象调用了方法,线程也创建了,但是线程没有执行
for(int i=0;i<10;i++){
System.out.print("线程2");
Thread.sleep(100);
}
}
}
class SynThread extends Thread{
//覆盖Thread类中的run方法
public void run(){
for(int i=0;i<10;i++){
System.out.print("线程1");
try {
//让线程睡眠100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}
由上面的程序可见:
每次运行的结果都是不相同的,其实多线程的运行就好比两个线程在抢夺cpu的执行权。
这就是多线程的特性:随机性,谁先抢到谁执行,至于执行多长时间cpu说了算。
第二种(实现Runnable接口):
通过实现Runnable接口,覆盖接口中的run方法,
通过Thread类创建线程,并将实现了Runnable接口的子类对象传递给Thread类的构造函数。
public class ThreadDemo2 {
public static void main(String[] args) throws InterruptedException {
//创建任务类对象
MyRunnable mr=new MyRunnable();
//创建线程对象,将任务分派给线程对象;
Thread t=new Thread(mr);
//将线程对象开启
t.start();
//自己定义个循环打印B,并每打印一次睡眠200毫秒
for(int i=0;i<10;i++){
System.out.print("A");
Thread.sleep(200);
}
}
}
//创建一个类实现Runnabl接口
class MyRunnable implements Runnable{
//重写Runnable中的run方法
public void run(){
//自己定义一个循环打印A,没打印一次睡眠200毫秒,睡眠时有异常发生
for(int i=0;i<10;i++){
System.out.print("B");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程安全的问题原因:
当多条语句在操作同一个线程时,一个线程只执行了一部分,还没有执行完,另一个线程就进来参与执行,导致共享数据的错误;
解决办法:
对多条操作共享数据的语句,只能让一个线程执行完,在执行过程中,其他线程是不能参与;
解决线程安全问题: 让线程同步(synchroized)
格式:synchronized(锁对象)
{
//需要同步的代码块
}public class TicketDemo {
public static void main(String[] args){
TicketThread t=new TicketThread();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketThread implements Runnable{
Object obj=new Object();
private int ticket=1001;
public void run(){
while(true){
synchronized(obj){
if(ticket<=0){
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+".."+ticket--);
}
}
}
}
Thread.sleep():让当前线程修休眠指定的毫秒数
Thread.currentThread():获取当前线程;
同步:
共享数据不能是局部变量,因为局部变量,都是每个线程都有拷贝,共享数据是属性。
同步需要两个或者两个以上的线程。
多个线程执行必须是同一把锁。
同步的弊端:
当线程相当多时,因为每个线程都会判断同步上的锁,这样就降低了程序的运行效率;
同步函数:在函数上加上synchronized即可
例如: public synchronized void fun(){}
静态同步方法的监视器对象:共享数据就是所在的静态同步方法类中的.class 对象。例如:
public class A{
public static synchronized void fun{
//对于fun来说共享数据就是A.class;
}
}
单例懒汉式的隐患:
class SingleTest{
private SingleTest(){}
private static SingleTest single=null;
public static SingleTest st(){
if(single==null){
synchronized(SingleTest.class){
if(single==null){
single=new SingleTest();
}
}
}
return single;
}
}
双重判断,就解决了隐患;保证了对象的唯一性;
什么是死锁:
张三A电话亭想去B电话亭。
李四B电话亭想去A电话亭。
理解死锁:
a线程锁定一个资源,同时想获得b线程锁定的资源。
b线程锁定一个资源,同时想获得a线程锁定的资源。
通常死锁都是同步嵌套造成的。
线程间通讯:
使用wait(),notify(),notifyAll() 这三个方法称之为通讯方法。
同步和线程间通讯方法:
没有同步环境,就不能完成线程间的通讯;
如果你的通讯方法没有在同步块中或者同步方法中,会抛出异常,
只能使用监视器对象调用通讯方法;
每个线程对象都有一个线程监狱,执行到a.wait() 的线程会被关押到a的线程监狱中。
要想释放a对象,的线程监狱中的线程,那么需要调用a.notify();该方法只能保证从a线程监狱中释放一个出一个线程,但不能保证释放的是哪一个。
还可以使用a.notifyAll()方法释放,将线程监狱中所有的对象都释放。
被wait的线程不能自己恢复到就绪状态必须由其他线程调用同一监视器上的notify或者notifyAll方法来唤醒。
被wait的线程会释放线程对象锁。这样其他线程可以占用他的同步运行环境
被唤醒的线程恢复到了就绪状态,当再次获取监视器对象的锁后会在wait()处向下运行。
后台线程(守护线程)
jvm会在所有的线程结束后结束。
当所有的非后台线程结束后,所有的后台线程将集体自杀。
什么时候使用后台线程:
例如:垃圾回收器,在我们的main方法结束后(所有我们写的线程),垃圾回收器没有道理还自己运行,那么可以理解为垃圾回收器是个后台线程。
如何设置后台线程:
Thread t=.....
t.setDaemon(true)//必须在start之前执行;
t.start();
合并线程:
join()是Thread类的方法。
Thread t=.....
t.start();
t.join();//使当前线程等待t线程结束后,在向下运行;
终止线程:
void interrupt:把中断值修改为true;
boolean isInterrupted():获取中断值
static boolean interrupted():获取中断值,并将中断值修改为false;
----------------------------------android培训、java培训、期待与您交流!------------------------------