黑马程序员———多线程

——- android培训java培训、期待与您交流! ———-
1,关于多线程: 顾名思义,一个进程中的多个线程。
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元或进程执行的一条线索或路径。线程在控制着进程的执行。一个进程中至少有一个线程。
线程的开始:当要运行一个程序时,JVM首先会找到main函数,然后从main函数开始执行(也就是说,程序是从main函数开始运行的),此时,程序就成为一个进程,既然是进程肯定有线程的存在。此时的线程就是主线程,主线程会向下顺序执行代码。如果程序中存在一个庞大的循环语句,主程序就会一直在这里运行,直到循环语句结束,下面的代码才能被执行到。 这可能要花费较长的时间,影响了程序运行的效率。所以,为了提高程序的效率,就引入了多线程。由主线程开辟另一个或多个线程,让这些线程都去执行代码。

2,创建线程有两种方式第一种方式:继承Thread类。
步骤:1、定义类继承Thread.
2、复写Thread类中的run方法。目的:将自定义方法存储在 run方法中,让线 程运行。
3、用子类new一个线程对象,线程对象调用线程的start方法来开启线程。该方法两个作用:启动线程,调用run方法。
例代码如下:
class Demo extends Thread
{
public void run()
{
System,out,println(“执行的代码”);
}
}
class ThreadDemo
{
public static void main(Stirng []args);
{
Demo d =new Demo();
d start();
}
}

3,创建线程的第二种方式:实现Runnable接口
步骤:1、定义类实现Runnable接口。
2、覆盖Runnable接口中的run方法。
3、通过Thread类建立线程对象。
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
例代码如下:
class Demo implements Runnable
{
public void run()
{
System.out.println(“Hello World”);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Thread t=new Thread(new Demo());
t.start();
总结:
两种方法的比较:Java中的类只能继承一个类,第一种方法中,当Demo继承了Thread之后,就不能再继承其 他类,但Demo很有可能是另一个类的子类,这时就出现了局限。第二种方法中,Demo实现了接口Runnable, 同时还能成为其他类的子类,使用较多。单核状态下,在某一时刻只有一个线程在运行。某一时刻可能有多个进程, 每个进程中也可能有多个线程,CPU就在这些线程间来回的切换,线程也只有在获得CPU的执行权时才能运行。
4, 线程的四个运行状态:
1.运行:既有执行资格又有执行权。
2.堵塞(临时状态):有执行资格没有执行权。等待CPU的执行权,一旦获得就进入运行状态。
3.冻结:没有执行资格也没有执行权。CPU不会分配执行权给它,直到它被唤醒。
从运行状态进入冻结状态:sleep(time)或wait();
被唤醒:sleep的时间到或notify().
4.消亡:run方法结束,或stop()。

5、多线程的安全问题:
什么时候会出现线程安全问题?
满足3个条件时,会出现:
1.存在多个线程
2.多个线程共享数据(共享数据一般是成员变量,局部变量不是)。
3.操作共享数据的语句至少要有两条。
6、多线程安全问题的解决:同步。因为同步可以保证多线程代码只能被持有锁的线程运行,其它线程不能运行。
任意时刻,一个锁只能被一个线程拥有。
(持有锁的线程会在执行完多线程代码时释放锁,这样锁就能被其它线程拥有。)
1.同步代码块;将要同步的代码放synchronized中,并加锁。
synchronized(对象)//只要是个对象就行,这个对象就是锁
{
要被同步的代码;
}
2.同步函数:synchronized能将代码封装并同步,函数只能将代码封装,所以用synchronized修饰函数,让函数既能封装又能同步。
同步函数也有锁。非静态同步函数的锁是this,同步函数所属对象的引用;静态同步函数的锁是:
类名.class,函数, 所属类的字节码文件对象。
需注意的是:1.不需要同步的代码不要放入同步函数中。2.synchronized放在函数返回值类型前
public synchronized void show()
{
要被同步的代码;
}
找到 要被同步的代码 的方法:
1.先找到 多线程所有要运行的代码(可能是一个run方法被多个线程使用同,也可能是一个run方法只被其中的一个线程使用);
2.多线程的共享数据(一般成员变量都是);
3.多线程要运行的代码中操作 共享数据 的代码就是 要被同步的代码。
注意:有时使用了同步仍然不成功,出现这种现象的原因可能是:需要被同步的代码可能放在程序的不同位置,有的代码要用到同步代码块,有的要用同步函数
那么怎么才能使同步成功?
1.首先判断要被同步的代码是否正确,确定所有要被同步的代码都被同步了。
2.同步代码块和同步函数中的锁是同一个锁,即必须是 多个线程使用同一个锁。。

7、同步时要避免的问题:死锁。
同步嵌套同步时会发生死锁。
if(flag)
{
while(true)
{
synchronized(Lock1)
{
System.out.println(“…….”);
synchronized(Lock2)
{
System.out.println(“!!!!!”);
}
}
}
}
else
{
while(true)
{
synchronized(Lock2)
{
System.out.println(“…….”);
synchronized(Lock1)
{
System.out.println(“!!!!!!!”);
}
}
}
}
当线程1满足if条件,只执行到 System.out.println(“…….”); 间片到了,线程2获取执行权,并满足else条件时,执行到一半,时间片也到了,
此时,线程一持有锁Lock1,线程二持有锁Lock2,都在等待对方执行完后释放锁,但是都无法执行完,这时就会形成死锁。
8、线程间通信;多个线程共享一段数据,但是对数据的操作不同。
等待唤醒机制:wait();notify();
notifyAll();三个方法都是Object类中的方法,都用来操作持有锁的线程,所以都只能用于同步。
notify()方法,唤醒内存中等待线程池中的第一个在等待的线程。
方法的使用:1.这三个方法在使用时,都必须标明它们将要操作的线程持有的锁,
即lock.wait()、lock.notify()、lock.notifyAll()
2.等待和唤醒只能是同一个锁。即,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒。
不可以对不同锁中的线程进行唤醒。
为什么三个方法都在Object中定义?
因为使用这三个方法在使用时,都通过锁来调用,而锁可以是任意对象,所以要定义到Object中。
一个生产者一个消费者:if notify
多个生产者多个消费者:whilenotifyAll
升级的多个生产者多个消费者,JDK1.5新特性:1.synchronized被Lock替代—>lock.lock()获取锁lock.unlock()释放锁
2.wait、notify、notifyAll三个方法被Condition代替—>condition.await()等待,condition.signal()唤醒一个线程,condition.signalAll()唤醒所有线程。
通过不同的Condition实例condition_con.signal() condition_pro.signal(),可以实现 只唤醒消费者线程,旧方法只能唤醒所有线程。
获取Condition实例,用Lock中的方法,lock.newCondition();
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
// t1 t2
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock();
try
{
while(flag)
condition_pro.await();
this.name = name+”–”+count++;
System.out.println(Thread.currentThread().getName()+”…生产者..”+this.name);
flag = true;
condition_con.signal();
}
finally
{
lock.unlock();//释放锁的动作一定要执行。
}
}
public void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+”…消费者………”+this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set(“+商品+”);
}
catch (InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}
停止线程:有两种方法—>run方法结束、stop()
stop方法已经过期,所以只能使run方法结束。
分析:程序中之所以使用多线程,通常是因为出现了较大的循环结构,即,多线程运行的代码通常是循环结构。
所以,只要控制住循环,就可以使run方法结束,也就结束了线程。使用标记。
特殊情况:当线程处于冻结状态(wait()了),就不会读取到标记,那么线程就无法结束。
这时,可以使用Thread中的interrupted()中断线程,中断线程是将线程从冻结状态强制恢复到运行状态,清除掉了冻结状态,这样就能继续读取标记。
9、Thread类中的几个方法
1.setDaemon():可以将线程标记为守护线程,该方法必须在启动线程前调用—->thread.setDaemon(); thread.start();
守护线程可以理解为后台线程,后台线程在开启之后会和前台线程共同抢占cup执行权,当所有前台线程都结束时,后台线程会自动结束。这样也可以实现停止线程。
2.join():等待该线程终止.当A线程执行了B线程的.join()方法时,A就会等待,等B线程都执行完,A才会执行。–>b.join();
若B线程执行到中间wait了,那么A就会一直等,这时可以使用b.interupt()强行使B线程切换到运行状态。
3.setPriority():设置线程的优先级。线程的优先级表示抢占CPU的频率,值越大优先级越高。共有10个等级,从1到10,所有线程默认的优先级是5。
Thread类中,1,5,10分别用静态常量表示:MIN_PRIORITY、MORM_PRIORITY、MAX_PRIORITY
使用—>thread.setPriority(MAX_PRIORITY);thread.setPriority(3);

需求:简单的卖票小程序,多个窗口同时卖票。(第二种方式)

class Ticket implements Runnable
{
private int tick=100;
public void run()
{
while(true)
{
if (tick>0)
{
System.out.println(Thread.currentThread().getName()+”~~~~sale:”+tick–);
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
t2.start();

}  

}
Java对于多线程的安全问题提供了专业的解决方式,一个是同步代码块synchronized。
再有就以上也说到了是JDK1.5提供了多线程升级解决方案。将synchronized替换成显式的Lock操作。将Object中的wait,notify,notifyAll替换成了Condition对象,该对象可以通过Lock锁进行获取。
不同例题多线程安全问题的例题如下:
import java.util.concurrent.locks.*;
class ProduceConsumerDemo
{
public static void main(String[] args)
{
Resource r=new Resource();
Producer pro=new Producer(r);
Consumer con=new Consumer(r);
Thread t1=new Thread(pro);
Thread t2=new Thread(pro);
t1.start();
t2.start();

}  

}
class Resource
{
private String name;
private int count=1;
private boolean flag=false;
private Lock lock=new ReentrantLock();
private Condition condition_pro=lock.newCondition();
private Condition condition_con=lock.newCondition();
public void set(String name) throws InterruptedException
{
lock.lock();
try
{
while(flag)
condition_pro.await();
this.name=name+”–”+count++;
System.out.println(Thread.currentThread().getName()+”…生产者”+this.name);
flag=true;
condition_con.signal();
}
finally
{
lock.unlock();
}
}
public void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+”,,,、、、、消费者、、、”+this.name);
flag=false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
try
{
res.set(“+商品+”);
}
catch (InterruptedException e)
{
}

    }  
}  

}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}

——- android培训java培训、期待与您交流! ———-

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
黑马程序员多线程练习题主要包括两个问题。第一个问题是如何控制四个线程在打印log之前能够同时开始等待1秒钟。一种解决思路是在线程的run方法中调用parseLog方法,并使用Thread.sleep方法让线程等待1秒钟。另一种解决思路是使用线程池,将线程数量固定为4个,并将每个调用parseLog方法的语句封装为一个Runnable对象,然后提交到线程池中。这样可以实现一秒钟打印4行日志,4秒钟打印16条日志的需求。 第二个问题是如何修改代码,使得几个线程调用TestDo.doSome(key, value)方法时,如果传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果。一种解决方法是使用synchronized关键字来实现线程的互斥排队输出。通过给TestDo.doSome方法添加synchronized关键字,可以确保同一时间只有一个线程能够执行该方法,从而实现线程的互斥输出。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [黑马程序员——多线程10:多线程相关练习](https://blog.csdn.net/axr1985lazy/article/details/48186039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值