------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、多线程
1.创建线程:
创建线程方式第一种:继承Thread类。
步骤:
(1)定义一个类继承Thread类。
(2)覆盖Thread类中的run方法。
(3)直接创建Thread的子类对象创建线程。
(4)调用start方法开启线程并调用线程的任务run方法执行。
<span style="font-size:18px;">public class Thread1 extends Thread{
public static void main(String[] args) {
Thread thread1= new Thread1();
Thread thread2= new Thread1();
thread1.start();
thread2.start();
}
public void run() {
}
}</span>
可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)
主线程的名字就是main。
开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法。
将运行的码定义在run方法中即可。
run方法就是封装自定义线程运行任务的函数。run方法中定义就是线程要运行的任务代码。
当前运行线程名称Thread.currentThread().getName()
Stop() 线程任务的结束
Sleep(time) 线程冻结,时间到了自动运行
wait() notify()唤醒
创建线程的第二种方式:实现Runnable接口。
1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务。
4,调用线程对象的start方法开启线程。
<span style="font-size:18px;">Class Demo implements Runnable {
Public void run( ){…};
public static void main(String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span></span>
实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。 按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。
线程安全问题产生的原因:
1,多个线程在操作共享的数据。
2,操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。
就会导致线程安全问题的产生。
解决思路:
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,
其他线程时不可以参与运算的。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码 ;
}
同步的好处是解决了线程的安全问题,相对降低了效率,因为同步外的线程都会判断同步锁
同步的前提:同步中必须有多个线程并使用同一个锁。
同步函数的使用的锁是this;
同步函数和同步代码块的区别:
(1)同步函数的锁是固定的this。
(2)同步代码块的锁是任意的对象。
(3)建议使用同步代码块。
静态的同步函数使用的锁是 该函数所属字节码文件对象
可以用 getClass方法获取,也可以用当前 类名.class 表示。
单例:加入同步为了解决多线程安全问题。
加入双重判断是为了解决效率问题。
<span style="font-size:18px;">Class Single{
private static Single s =null;
private single(){ }
public static Single getInstance(){
If(s==null){
synchronized(Single.class){
if(s==null)
s= new Single();
}
}
return s;
}
}
死锁:常见情景之一:同步的嵌套。
Class Test implements Runnable{
Private int num =100;
Object obj = new Object();
Private boolean flag =true;
Public void run(){
if(flag){
while(true){
synchronized(obj){
show();
}
}
}
else
while(true)
this.show();
}
Public synchronized void show(){
Synchronized(obj){
If(num>0){
Try{Thread.sleep(10);}catch(InterruptedException e){}
}
}
}
public static void main(String[] args) {
Test t = new Test();
Thread t1= new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(InterruptedException e){}
t.flag = false;
t2.start();
}
}</span>
1,wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。
2,notify():唤醒线程池中一个线程(任意).
3,notifyAll():唤醒线程池中的所有线程。
注意“:
(1)这些方法都必须定义在同步中。因为这些方法是用于操作线程状态的方法。
必须要明确到底操作的是哪个锁上的线程。
(2)if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行!
(3)notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。
Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。同时更为灵活。可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,通常需要定义finally代码块中。
wait 和 sleep 区别?
(1)wait可以指定时间也可以不指定。
sleep必须指定时间。
(2)在同步中时,对cpu的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
停止线程:
1,stop方法。2,run方法结束。
怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。控制循环通常就用定义标记来完成。
但是如果线程处于了冻结状态,无法读取标记。
如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。 当时强制动作会发生了InterruptedException,记得要处理
t1.join();//t1线程要申请加入进来,运行。临时加入一个线程运算时可以使用join方法。
Yield() 暂停当前线程,执行其他线程