------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
多线程的概念:
首先介绍什么是进程:进程是正在执行的程序,每个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
线程是其实就是进程中一个独立的控制单元,线程在控制着执行,一个进程至少有一个线程。
线程和进程的不同:
①地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;;
②资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源;
③线程是处理器调度的基本单位,但进程不是;
线程和进程的优缺点:
线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
多线程的好处:
在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(,因此创建多个线程去执行一些任务会比创建多个进程更好。
多线程的特点:
随机性,谁先抢到谁先执行,执行时间有CPU决定。
线程创建的两种方法:
方法一:继承Thread类
步骤:
①定义类继承Thread类;
②复写Thread类中的run方法,将要让线程运行的代码都存储到run方法中;
③通过创建Thread类的子类对象,创建线程对象;
④调用线程的start方法,开启线程,并执行run方法 ;
继承Thread类的代码示例:
public class Demo extends Thread {
public void run() {
for (int i = 0; i < 60; i++)
System.out.println("Demo run" + i);
}
}
class ThreadDemo {
public static void main(String[] args) {
Demo d = new Demo();// 创建一个线程
d.start(); // start启动线程
for (int i = 0; i < 60; i++)
System.out.println("main " + i);
}
}
方法二:实现Runnable接口
步骤:
①定义类实现Runnable接口
②覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中;
③通过Thread类建立线程对象;
④将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;
⑤调用Thread类的start方法开启线程;
实现Runnable接口的代码示例
public class MyThread {
<span style="white-space:pre"> </span>public static void main(String[] args) {
<span style="white-space:pre"> </span>Ticket t = new Ticket();
<span style="white-space:pre"> </span>// 线程开始
<span style="white-space:pre"> </span>new Thread(t).start();
<span style="white-space:pre"> </span>new Thread(t).start();
<span style="white-space:pre"> </span>}
}
// 实现Runnable接口
class Ticket implements Runnable {
<span style="white-space:pre"> </span>private int t = 100;
<span style="white-space:pre"> </span>// 覆run方法
<span style="white-space:pre"> </span>public void run() {
<span style="white-space:pre"> </span>while (true) {
<span style="white-space:pre"> </span>if (t > 0) {
<span style="white-space:pre"> </span>System.out.println(Thread.currentThread().getName() + ":::"
<span style="white-space:pre"> </span>+ t--);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
}
实现Runnable接口可以避免单继承的局限性,最常使用实现方式。
区别:继承Thread:线程代码存放Thread子类run方法中。实现Runnable:线程代码存在接口的子类run方法中。
线程一共分为5种状态:
①被创建new
②sleep 睡眠状态,(time)给他一个参数,时间一到就会恢复到临时状态;
③wait(等待), 直到被 notify(通知)才会回到临时状态;
④stop(消亡状态) run方法结束了线程也会消亡;
⑤临时状态或者被叫(阻塞状态),它具备运行资格,但是没有执行权)。线程被start之后 在等待CPU的执行权;
线程状态图
线程安全:
线程安全产生的原因:多个线程共同操作同一个共享数据;有多条语句对共享数据进行计算,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决方案:同步代码块或同步函数。
同步代码块的格式
synchronized(对象) { //任意对象都可以。这个对象就是锁。
需要被同步的代码;
}
同步函数: 所谓的同步函数就是在函数的返回值前面加一个synchronized关键字就是同步函数了。
同步代码块的锁是参数对象,
同步函数的锁是this,
静态同步函数的锁是该类的字节码文件对象。
在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。
同步线程的前提:
①必须要有两个或者两个以上的线程。
②必须是多个线程使用同一个锁。
③必须保证同步中只有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
单例设计模式的懒汉式:
对象是方法被调用时才初始化,也叫对象的延时加载 。下例中Single类进入内存,对象还没有存在,只有调用了getInstance方法时,才建立对象的懒汉式;
懒汉式的特点就是延迟加载, 不过还是存在问题,如果多线程时会出现安全问题,可以加同步代码块解决问题,加同步时用的锁必须是该类所属的字节码文件对象。
懒汉式代码示例:
/*
* 延迟加载模式——懒汉式
* 加入同步机制,解决安全问题。但是却带来了效率降低。
* 为了效率问题,通过双重判断的形式解决。
*/
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 boolean flag;
Test (boolean flag)
{
this.flag=flag;
}
public void run ()
{
if (flag)
{
synchronized(MyLock.locka)//a锁
{
System.out.println("if locka");
synchronized(MyLock.lockb)//b锁
{
System.out.println("if lockb");
}
}
}
else
{
synchronized(MyLock.lockb)//b锁
{
System.out.println("else lockb");
synchronized(MyLock.locka)//a锁
{
System.out.println("else locka");
}
}
}
}
}
//定义两种锁的类
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLockDemo
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------