java基础之多线程技术

来源:互联网 发布:网络诈骗手段方式 编辑:程序博客网 时间:2024/06/14 16:12

1.线程的概述:

进程:正在进行中的程序(直译)。
线程:就是进程中一个负责程序执行的控制单元(执行路径),
一个进程中可以多执行路径,称之为多线程。
一个进程中至少要有一个线程。
开启多个线程是为了同时运行多部分代码。
每一个线程都有自己运行的内容。这个内容可以称为线程要执行的任务。

多线程好处:解决了多部分同时运行的问题。
多线程的弊端:线程太多回到效率的降低。

其实应用程序的执行都是cpu在做着快速的切换完成的,这个切换是随机的。
JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。
1,执行main函数的线程,该线程的任务代码都定义在main函数中。
2,负责垃圾回收的线程。

2.创建线程的方式

  • 创建线程方式一:继承Thread类。
  • 创建线程方式二:实现Runnable接口

继承Thread类
步骤:
1,定义一个类继承Thread类。
2,覆盖Thread类中的run方法。
3,直接创建Thread的子类对象创建线程。
4,调用start方法开启线程并调用线程的任务run方法执行。
可以通过Thread的getName获取线程的名称Thread-编号(从0开始)
主线程的名字就是main。

继承Thread类来创建一个线程public class Demo11 {    public static void main(String[] args) {        Thread1 t= new Thread1();        //t.run();//仅仅是调用了重写的run方法        t.start();//开启线程并执行该线程的run方法。        for (int i = 0; i < 60; i++) {            System.out.println("main -- " + i);        }    }}class Thread1 extends Thread {    public void run() {        for (int i = 0; i < 60; i++) {            System.out.println("Thread1 -- " + i);        }    }}在控制台可以看到主线程和当前的线程交替输出内容。

我们发现运行结果每一次都不同。
因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。

这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。

为什么要覆盖run方法呢?
Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代码。

创建线程的第二种方式:实现Runnable接口。
步骤:
1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
所以要在线程对象创建时就必须明确要运行的任务。
4,调用线程对象的start方法开启线程。

实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。
所以,创建线程的第二种方式较为常用。

例如:

public class Demo12 {    public static void main(String[] args) {        ThreadImpl t = new ThreadImpl();        Thread thread = new Thread(t);        thread.start();    }}class ThreadImpl implements Runnable {    @Override    public void run() {        for (int i = 0; i < 20; i++) {            System.out.println("Thread--" + i);        }    }   }

3.多线程典型案例之银行存钱

需求:银行有一个金库。有两个储户每次存100,存3次。

目的:该程序是否有安全问题,如果有,如何解决?

如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。

class Bank{    private int sum;    //Object obj = new Object();    public synchronized void add(int n)//加入同步锁机制   这是一个同步函数    {        //synchronized(obj)        //{            sum = sum + n;            try{Thread.sleep(10);}catch(Exception e){}            System.out.println("sum="+sum);        //}    }}class Cus implements Runnable{    private Bank b = new Bank();    public void run()    {               for(int x=0; x<3; x++)        {            b.add(100);        }    }}class  BankDemo{    public static void main(String[] args)     {        Cus c = new Cus();        Thread t1 = new Thread(c);        Thread t2 = new Thread(c);        t1.start();        t2.start();    }}

4.多线程典型案例之卖票

下面这个程序会出现多个窗口卖同一张票的事情。还有可能打印出0,-1,-2等错票。
多线程的运行出现了安全问题。

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);//创建了一个线程;        Thread t3 = new Thread(t);//创建了一个线程;        Thread t4 = new Thread(t);//创建了一个线程;        t1.start();        t2.start();        t3.start();        t4.start();    }}

上面问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

Java对于多线程的安全问题提供了专业的解决方式。就是同步代码块。

同步代码块的格式:synchronized(对象){    需要被同步的代码}

加了同步代码块之后:

class Ticket implements Runnable{    private  int tick = 1000;    Object obj = new Object();    public void run()    {        while(true)        {            synchronized(obj)            {                if(tick>0)                {                   System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);                }            }        }    }}class  TicketDemo2{    public static void main(String[] args)     {        Ticket t = new Ticket();        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();    }}

对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

例如:火车上的卫生间

同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。

多线程同步的好处和弊端
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。

5.同步代码块和同步函数

同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。

通过该程序进行验证。
使用两个线程来买票。
一个线程在同步代码块中。
一个线程在同步函数中。
都在执行买票动作。


静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class

class Ticket implements Runnable {    private  int tick = 100;    //Object obj = new Object();    boolean flag = true;    public  void run() {        if(flag) {            while(true) {                synchronized(this) { //synchronized(Ticket.class)                    if(tick>0) {                        try{Thread.sleep(10);}catch(Exception e){}                        System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);                    }                }            }        }        else            while(true)                show();    }    public synchronized void show()//this    {        if(tick>0)        {            try{Thread.sleep(10);}catch(Exception e){}            System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);        }    }}class  ThisLockDemo{    public static void main(String[] args)     {        Ticket t = new Ticket();        Thread t1 = new Thread(t);        Thread t2 = new Thread(t);        t1.start();        try{Thread.sleep(10);}catch(Exception e){}        t.flag = false;        t2.start();    }}

6.多线程之死锁

死锁产生的原因:同步中嵌套同步。

class Test implements Runnable{    private boolean flag;    Test(boolean flag)    {        this.flag = flag;    }    public void run()    {        if(flag)        {            while(true)            {                synchronized(MyLock.locka)                {                    System.out.println(Thread.currentThread().getName()+"...if locka ");                    synchronized(MyLock.lockb)                    {                        System.out.println(Thread.currentThread().getName()+"..if lockb");                                      }                }            }        }        else        {            while(true)            {                synchronized(MyLock.lockb)                {                    System.out.println(Thread.currentThread().getName()+"..else lockb");                    synchronized(MyLock.locka)                    {                        System.out.println(Thread.currentThread().getName()+".....else locka");                    }                }            }        }    }}//定义两个锁对象class MyLock{    static Object locka = new Object();    static Object lockb = new Object();}class  DeadLockTest{    public static void main(String[] args)     {        Thread t1 = new Thread(new Test(true));        Thread t2 = new Thread(new Test(false));        t1.start();        t2.start();    }}

7.线程的四种状态

这里写图片描述

原创粉丝点击