Java多线程

1.创建多线程的方法

多线程的创建有四种方法

1.1 继承Thread类

具体有一下四步

1.创建一个继承于Thread类的子类

2.重写Thread类的run()方法

3.创建Thread子类的对象

4.通过此对象调用start()方法

具体的代码如下:

import java.lang.Thread;
class MyThread extends Thread{
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            if(i%2==0){
                System.out.print(i);
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args){
        MyThread t1 = new MyThread();

        t1.start();
	for(int i=0;i<10;i++)
        System.out.println("hello");
    }
}

上述代码中先创建了一个Mythread对象,这个对象中重写了run方法,是输出0-100之间的偶数,然后在main()中创建了M有thread的对象,并且调用start方法,start()方法有两个作用,1.启动线程,2.jvm调用当前线程的run方法,然后在main方法中输出十次hello.

执行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZUi5f7rL-1602745627805)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20201012172842238.png)]

可以看到run方法和main中hello的输出是交替进行的。

注意:不能通过run方法来启动线程,只能通过start方法,通过run不能够启动线程

1.2 使用匿名子类的方式

如果一个线程,我们只是简单调用一下,可以直接创建一个Thread类的匿名子类

代码如下:

public class ThreadTest {
    public static void main(String[] args) {
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    if (i % 2 != 0) {
                        System.out.println(Thread.currentThread().getName() + ':' + i);
                    }
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    if (i % 2 == 0) {
                        System.out.println(Thread.currentThread().getName() + ':' + i);
                    }
                }
            }
        }.start();
    }
}

在main中创建了两个Thread类的匿名子类,并重写了他们的run方法,然后直接调用,执行结果如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kKP5fyjQ-1602745627809)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20201012212052814.png)]

1.3 利用Runnable()接口

具体步骤如下:

1.创建一个实现了Runnable()接口的类

2.重写Thread类的run()

3.创建Thread类的子类对象

4.通过这个对象调用start()

import java.lang.Thread;
class MyThread1 implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+':'+i);
            }
        }
    }
}
public class ThreadTest {
    public ThreadTest() {
        super();
    }

    public static void main(String[] args) {
        //创建实现类的对象
        MyThread1 t0 = new MyThread1();
        //将此对象作为参数传递到Thread类的构造器中
       Thread t1 = new Thread(t0);
       //通过Thread类的对象调用start()
       t1.start();
       Thread t2 = new Thread(t0);
       t2.start();
    }
}

1.4 使用线程池

提前创建好多个线程,放入线程池,使用时直接获取,使用完再放回池中。可以避免频繁的创建和销毁,实现重复利用,能够提高先用速度,降低资源消耗,便于线程管理。

线程池的三个属性:

corePoolSize:核心池的大小

maximumPoolSize:最大线程数

keepAliveTime:线程没有任务最多保持多久后会终止

2. Thread类中有关的方法

start()

启动当前线程,并且调用当前线程的run()

run()

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

currentThread()

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

getName()

获取当前线程的名字

setName()

设置当前线程的名字

yield()

释放当前cpu的执行,让当前的线程让出cpu

join()

在线程A中调用线程B的join()方法,线程A阻塞,直到线程B结束,线程A才结束阻塞

sleep(millis )

调用这个方法的线程阻塞一段时间,时间由参数给定,单位是毫秒

isAlive()

判断这个线程是否存活

getPriority()

获取线程的优先级

setPriority(int p)

设置线程的优先级

3. 线程的优先级

java中已经设置好了三个表示优先级的常量,数越大则说明优先级越高

MAX_PRIORITY:10

MIN_PRIORITY:1

MORM_PRIORITY:5

线程默认的优先级为5,并不是优先级高的线程执行完优先级低的才执行,只是优先级高的线程被执行的概率更高

4.多线程的同步

java中通过同步的机制,来解决线程安全的问题,java实现线程的同步有三种方式

4.1 同步代码块

利用同步监视器,也就是锁,来同步代码块。

任何一个对象都可以都可以来当作锁,多线程必须用同一个所

package com.aiguigu.java;

class Window extends Thread{
    private int ticket = 100;
    @Override
    public void run(){
        while(true){
            if(ticket>0){
                System.out.println(getName()+":卖出的票号为"+ticket);
                ticket--;
            }
        }
    }
}
public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

在没有加锁的时候,多线程可能会引起各种安全问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f9hD8VSe-1602745627814)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20201013163515408.png)]

通过运行截图可以看到,票号为1的票被窗口3和窗口1卖出去了,相当于一张票卖了两遍,这显然是不行的。通过synchronize()方法来同步代码块就可以解决这个问题

class Window extends Thread{
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run(){
        while(true) {
            synchronized (obj) {
                if (ticket > 0) {
                    System.out.println(getName() + ":卖出的票号为" + ticket);
                    ticket--;
                } else break;
            }
        }
    }
}

public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SyUX99oT-1602745627818)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20201013163734686.png)]

可以看到,这样就不存在同一张票被卖两次的问题,也就是实现了进程的同步。需要注意的是,synchronized()方法可以用任何的对象来当做锁,但是必须用同一个对象来当做锁。

4.2 同步方法

即把需要同步的代码, 写成一个方法,在方法名前面加上synchronized关键字,将该方法声明为同步的

class Window extends Thread{
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run(){
        while(ticket>0) {
            show();
            }
       }
    public synchronized void show(){
        if(ticket>0){
            System.out.println(getName()+":卖出的票号为"+ticket);
            ticket--;
        }
    }
    }

public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

同步方法中,默认使用的锁为this对象。

5.线程的通信

设计到三个方法:

wait()

一旦执行此方法,当前线程就会进入阻塞状态,并且释放同步监视器

notify()

执行此方法,会唤醒一个被wait阻塞的线程。如果有多个线程被阻塞,就唤醒优先级高的那个

notifyall()

唤醒所有被wait的进程

这三个方法都必须使用在同步方法或者同步代码块中,这三个方法的调用者,必须是同步方法或者同步代码块中的同步监视器,否则会报错,这三个方法是被定义在object()对象中的。

注意:

sleep()和wait()的异同:

相同点:都能使得当前线程进入阻塞

不同点:1.sleep被声明在Thread类中,wait被声明在object对象中

​ 2.调用场景不同,sleep可以在任何需要的场景下调用,wait必须使用在同步方法或者同步代码块中
3.wait释放同步监视器,而sleep不释放

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值