Java-多线程

1.几个概念

1.程序:指唯一完成某个任务,用某种编程语言编写的一组指令的集合

2.进程:指程序的一次执行过程,例如我们启动了qq,word等,同时可以打开多个qq,就是说一个程序可以有多个进程

3.线程:进程的进一步细分,是一个程序内部执行的一条路径,一个进程可以有多个线程,一个线程就是一个指令,cpu调度的最小单位,由cpu一条一条的执行。一个进程同时执行多个线程,那么就是多线程。

4.并行:多个cpu同时执行多个任务

5.并发:一个cpu同时执行多个任务

2.多线程的创建

1.继承Thread类

1.步骤

1.让某个类成为Thread类的子类

2.重写Thread类中的run方法,将要让该线程执行的内容写在该方法中

3.创建Thread类的对象后,调用start()方法,启动线程

class 线程类 extends Thread{

public void run(){

线程的任务

}

}

public static void main(String[] args){

线程类 线程类对象 = new 线程类();

线程类对象.start();

}

public class Windows extends Thread {
    private int ticket=100;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"正在卖第"+(101-ticket));
            ticket--;
        }
    }
    public static void main(String[] args) {
        Windows w = new Windows();
        Thread t1 = new Thread(w,"窗口1");
        Thread t2 = new Thread(w,"窗口2");
        Thread t3 = new Thread(w,"窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

2.注意:

如果我们要通过继承 Thread 类去实现多线程,那么我们就需要重写 Thread 类中的 run方法,然后去构建继承了 Tihread 类的对象,最后通过调用 start 方法去开启多线程。如果直接调用 run 方法就相当于在主线程 main 中调用一个类的普通的方法,并不会重新开启一个线程。

对于线程的开启我们还需要注意的是一个线程类只能调用一次 start 方法否则会抛出IllegalThreadStateException 异常。

3.Thread类常用方法

start():1.启动当前线程 2.调用线程中的 run 方法

run():通常需要重写 Thread 类中的此方法,将创建的线程要执行的操作声明在此方法中

currentThread():静态方法,返回执行当前代码的线程

getName():获取当前线程的名字

setName():设置当前线程的名字

yield():主动释放当前线程的执行权

join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去

stop():过时方法。当执行此方法时,强制结束当前线程。

sleep(long millitime):线程休眠一段时间(毫秒)

2.实现Runable接口

class 线程类 implements Runable{

public void run(){

线程的任务

}

}

public static void main(String[] args){

线程类 线程类对象 = new 线程类();

线程类对象.start();

}

public class Windows implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"正在卖第"+(101-ticket));
            ticket--;
        }
    }
    public static void main(String[] args) {
        Windows w = new Windows();
        Thread t1 = new Thread(w,"窗口1");
        Thread t2 = new Thread(w,"窗口2");
        Thread t3 = new Thread(w,"窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

3.实现Callable接口

Callable与Runable类似,只是Callable的call方法有返回值

FutureTask可以用于接收Callable对象

FutureTask还提供了一个get方法用于拿取返回值

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Model4 {
    public static void main(String[] args) {
        Th1 t = new Th1();
        FutureTask futureTask = new FutureTask(t);
        FutureTask futureTask2 = new FutureTask(t);
        new Thread(futureTask).start();
        new Thread(futureTask2).start();
    }
}
class Th1 implements Callable {
    private int num = 1;
    @Override
    public Object call() throws Exception {
        while (true){
            synchronized (this){
                notify();
                if (num <= 20){

                    System.out.println(Thread.currentThread().getName()+"--->"+num);
                    num++;
                    wait();
                }else {
                    break;
                }
            }
        }
        return null;
    }
}

3.线程的调度模式

cpu会给线程分配时间片,一但线程得到了时间片,他就可以执行,哪个线程抢到时间碎片,哪个就先执行,没有抢到时间片的就处于等待状态。

多线程的调度方式有两种即分时调度模式和抢占调度模式,其中分时调度模式是指让所有线程轮流获得 cpu 的使用权,并且分配的使用时间是平均的,而抢占模式则是指的是每条线程执行的时间、线程的切换都由系统控制,系统控制指的是在系统某种运行机制下,可能每条线程都分同样的执行时间片,也可能是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的堵塞不会导致整个进程堵塞。

3.1线程的周期

 

4.线程安全

前面我们去实现了抢票案例,但是我们发现窗口一、窗口二、窗口三在售卖同一张票,造成这种现象主要是一个线程还没结束,另外一个线程就参与进来,从而造成三个窗口操作同一张票从而出现重票。

对于上述三个窗口售票异常我们也可以称之为线程安全问题,也就是当多个线程去操作共享数据的时候出现了共享数据的冲突,此时线程时不安全的。

4.1使用线程安全类

4.2Synchronized同步锁

这个关键字可以修饰方法或代码块。

synchronized(锁对象){

代码

}

修饰方法:写在方法的返回值前。这样该方法就称为同步方法,在执行的时候,其他线程要排队等待该方法执行完毕后才执行。

public synchronized void fun(){

   //要同步的代码

}


//同步代码块

public class Model8 {
    public static void main(String[] args) {
        WindowSal sal = new WindowSal();
        Thread thread1 = new Thread(sal,"窗口一");
        Thread thread2 = new Thread(sal,"窗口二");
        Thread thread3 = new Thread(sal,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class WindowSal implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if (ticket > 0){
                    System.out.println(Thread.currentThread().getName()+"正在售卖第 "+(100-ticket+1)+"张票"+"还剩下:"+(ticket-1)+"张票。");
                            ticket--;
                }else {
                    break;
                }
            }
        }
    }
}

//同步方法

public class Windows {
    public static void main(String[] args) {
        WindowSal sal = new WindowSal();
        Thread thread1 = new Thread(sal,"窗口一");
        Thread thread2 = new Thread(sal,"窗口二");
        Thread thread3 = new Thread(sal,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class WindowSal implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
        while (true){
            sal();
            if (ticket<=0){
                break;
            }
        }
    }
    private synchronized void sal(){
        if (ticket > 0){
            System.out.println(Thread.currentThread().getName()+"正在售卖第"+(100-ticket+1)+"张票"+"还剩下:"+(ticket-1)+"张票。");
                    ticket--;
        }else {
            return;
        }
    }
}

Synchronized特点

1)原子性,里面的代码不可分割,一起执行,一起不执行

2)有序性:被包裹的代码在多线程的条件下,只能由一条线程访问

3)可见性:每次操作其中的数据时,操作的是副本,而不是本体

4.3Lock

Lock();加锁

trylock(long 时间,时间单位);跟Lock();一样,加入了时间,但是有返回值,规定时间内加锁成功返回true,失败返回flase

unLock();释放锁

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Model3 {
    public static void main(String[] args) {
        T2 t1 = new T2("线程一");
        T2 t2 = new T2("线程二");
        t1.start();
        t2.start();
    }
}
class T2 extends Thread{
    private static Lock lock = new ReentrantLock();
    private static Condition c1 = lock.newCondition();
    private static int num = 1;
    T2(String name){
        super(name);
    }
    @Override
    public void run() {
        while (true){
            try {
                lock.lock();
                c1.signal();
                if (num <= 20){
                    System.out.println(getName()+"--->"+num);
                    num++;
                    c1.await();
                }else {
                    break;
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
}

线程通信:利用wait()、join()、sleep()、notify()等方法实现线程中的通信

wait()-----使当前线程进入阻塞状态,并且释放锁,wait()需要抛出一个异常InterruptedException

notify()-----用于唤醒另外一个被wait()的线程。notifyall()----用于唤醒所有wait()线程。

wait()和notify()需要放在Synchronized同步方法或者代码块里面,通过锁对象调用wait()和notify()

wait();和sleep();区别

1.sleep();不会释放锁,wait();会释放

2.sleep();到时间自动苏醒,wait();需要手动唤醒

public class Tongxin {
    public static void main(String[] args) {
        Tt tt1 = new Tt("线程一");
        Tt tt2 = new Tt("线程二");
        tt1.start();
        tt2.start();
    }
}
class Tt extends Thread {
    private static int num = 1;
    private static Object o = new Object();
    public Tt() {

    }

    public Tt(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (true) {
            synchronized (o) {
                o.notify();//-----notify()用于唤醒另外一个被wait()的线程
                if (num <= 20) {
                    System.out.println(getName() + num);
                    num++;
                    try {
                        o.wait();// ---使当前线程进入阻塞状态,并且释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }

    }
}

Lock和Synchronized区别

1、Synchronized是关键字,Lock是一个接口

2、Synchronized的加锁和释放锁是自动的,Lock则相反

3、Lock可以监听到它的锁状态

4、Synchronized会造成死锁,Lock则不会

4.4死锁

如有两个人吃西餐,必须要有刀和叉才能吃饭,只有一副刀叉。

如果A拿到了刀,B拿到了叉,互相都在等待另一个工具,但都不释放自己的,

这时就会造成死锁的局面,既不结束,也不继续。

造成死锁的原因:Synchronized使用不当

解决方法:1.改变加锁顺序 2.使用Lock锁

5.线程池

线程池是一种基于池化技术思想来管理线程的工具。在线程池中维护了多个线程,由线程池统一的管理调配线程来执行任务。通过线程复用,减少了频繁创建和销毁线程的开销。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值