首页 > 代码库 > 多线程基础
多线程基础
- 线程与进程的关系:
进程:是指在系统中正在运行的一个运用程序实例,它包括代码加载,执行和执行完毕一个完整的过程,也是进程本身从产生发展到消亡的过程。
线程:是比进程更小的执行单位,是在进程的基础上进行的进一步的化分,多线程是指一个进程在执行的过程中可以产生多个同时存在,同时运行的线程。
多线程的优点:多线程机制可以合理的利用资源,提高程序的运行效率(解决程序多部分同时运行的问题)。一个进程至少包含一个线程,程序运行时即自动产生一个线程。
多线程的弊端:线程太多会导致整体效率降低
- 如何创建一个线程:
创建线程方式一:继承Thread类
- 定义一个类继承Thread类
- 覆盖Thread类中的run 方法
- 直接创建Thread的子类对象创建线程
- 调用start方法开启线程,并调用线程任务的run方法执行
可以通过Thread类的getName方法获取线程的名称,Thread-编号(从零开始)
public class Test extends Thread {
private String name;
public Test(String name)
{
this.name=name;
}
public void run()//运行线程
{
for(int i=1;i<=15;i++)
{
System.out.println(name+"运行,i="+i);
}
}
public static void main(String [] args)
{
Test a= new Test("线程A");//创建一个线程对像
a.start();//开启运行线程
Test b= new Test("线程B");
b.start();
}
}
创建线程的第二种方式:实现Runnable接口
1. 定义类实现Runnable接口
- 覆盖接口中的run方法,将线程中的任务代码封装到run方法中。
- 通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread的构造函数的参数进行传递。
- 调用线程对象的start方法,开启线程。
例如:
public class Test implements Runnable{
private String name;
public Test(String name)
{
this.name=name;
}
public void run()
{
for(int i=1;i<=15;i++)
{
System.out.println(name+"运行,i="+i);
}
}
public static void main(String [] args)
{
Test a= new Test("线程A");
Test b=new Test("线程B");
Thread a1=new Thread(a);
Thread b1=new Thread(b);
a1.start();
b1.start();
}
}
实现Runnable接口的好处:
1. 将线程的任务从线程的子类中分离出来,进行了单独的封装。按照面向对象的思想将任务封装成了对象。
- 避免了java的单继承的局限性。
- 适合多个具有相同程序代码的线程处理同一资源。
所以创建线程的第二种方式比较常用。
线程安全问题产生的原因:
- 多条线程在操作共享的数据。
- 操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码的过程中其他线程参与了运算就会导致线程安全问题。
在java中用同步代码块可以解决线程安全问题
同步代码块的格式:
synchronized(同步对象)
{
需要被同步的代码。
}
同步对象是指为对象加上资源锁,即一定时间范围内只允许一个线程使用当前的同步对象,其他线程必须等待当前线程使用结束后才能使用该对象,通常情况下将当前的this对象或Object对象。
同步的好处:解决了线程的安全的问题。
同步的弊端:相对降低了效率因为同步外的线程都会判断同步锁(判断同步对象)
同步的前提:同步中必须有多个线程并使用同一个同步对象。
也可以使用同步方法来解决线程安全问题:同步方法是指使用了synchronized关键字声明方法,即为方法加上资源访问锁同一时间只允许一个线程调用此同步方法,其他线程必须等待当前线程调用结束后才能调用此同步方法。
同步方法和同步代码块的区别: 同步函数的锁是this
同步代码块的锁是任意对象
首选同步代码块。
注意:静态同步函数使用的锁是该函数所属的字节码文件的对象,可以用getClass()方法获取,也可以使用类名.class来表示
死锁:死锁的常见情景之一同步的嵌套
多线程的等待唤醒机制:
方法:
wait(); 让线程处于冻结状态,被wait的线程会被存储到线程池中
notify();唤醒线程池中的一个线程
notifyAll(); 唤醒线程池中的所有线程
这些方法都必须定义在同步中,因为这些方法都是用于操作线程状态的方法,必须明确到底要操作的是哪个锁上的线程。
jdk1.5以后将同步和锁封装成了对象,并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
Lock接口:的出现替代了同步代码块或同步函数,将同步的隐式锁变成现实锁操作,同时更为灵活, 在一组锁上添加多组监视器
lock();获取锁
unlock();释放锁,同时需要放到finally代码块中
Condition接口:的出现替代了Object中的wait notify notifyAll方法将这些监视器方法单独进行了封装,变成了Condition监视器对象,可以和任意锁进行组合
await(); 等同于wait();
signal();等同于notify();
signalAll();等同于notifyAll();
wait();和sleep();的区别?
- wait();可以指定时间,也可以不指定,sleep();必须指定时间
- 在同步中对cpu的执行权和锁的处理不同
- wait();释放执行权,释放锁
- sleep();释放执行权,不释放锁
多线程基础