java创建多线程的8种方式(一文搞懂Java多线程)

Java多线程的运行机制Java多线程的创建

Java多线程的创建有三种方式:

1.通过继承Thread类创建进程

2.通过Runnable接口创建线程

3.通过Callable接口和Future接口创建线程

1.通过继承Thread类创建进程

run() start()

Thread常用方法

方法

说明

void run()

线程运行时所执行的代码

void interript

中断进程

void start()

使该线程开始执行

static void yield()

暂停当前正在执行的进程 并执行其他进程

Thread.State getState()

返回该线程的状态

final boolean isAlive()

测试该线程是否处于活动的状态

String getName()

返回该线程的名称

void setName(String name)

改变线程的名称

public class www { public static void main(String[] args){ for (int i = 0; i < 3; i ) { Test test = new Test(i); test.start(); } } } class Test extends Thread{ int name; public Test(int name){ this.name = name; } public void run() { System.out.println("线程" name); } }

java创建多线程的8种方式(一文搞懂Java多线程)(1)

每次输出的结果可能不同,这是因为子线程执行的进度是不确定的,它们是并发运行的。

缺点 :若该类已经继承一个类,则无法继承 Thread 类。

2.通过Runnable接口创建线程
  • 步骤1:定义 Runnable 接口的实现类,并实现该接口的方法 run()
  • 步骤2:定义 Runnable 实现类的实例,并以此为实例作为 Thread 类的 target 参数,来创建 Thread 线程对象,该 Thread 对象才是真的线程对象。

public class www { public static void main(String[] args){ for (int i = 0; i < 3; i ) { Test test = new Test(i); Thread t = new Thread(test); t.start(); } } } class Test implements Runnable{ int name; public Test(int name){ this.name = name; } public void run() { System.out.println("线程" name); } }

java创建多线程的8种方式(一文搞懂Java多线程)(2)

同样每次输出的结果可能不同,这也是因为子线程执行的进度是不确定的,它们是并发运行的。

3.通过Callable接口和Future接口创建线程
  • 步骤1:创建 Callable 接口的实现类,并实现 call() 方法,该方法将作为线程的执行体,并且有返回值。
  • 步骤2:创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
  • 步骤3:使用 FutureTask 对象作为 Thread 对象的 target ,创建并启动新线程。
  • 步骤4:调用 FutureTask 对象的 get() 方法来获得子线程执行结束的返回值。

public class www { public static void main(String[] args){ for (int i = 0 ; i < 3 ; i ){ Test test = new Test(i); //使用FutureTask来包装Callable对象 FutureTask result = new FutureTask(test); //创建线程对象 Thread thread = new Thread(result); //启动线程 thread.start(); } } } class Test implements Callable{ int name; public Test(int name){ this.name = name; } @Override public Object call() throws Exception { System.out.println("线程" name); return null; } }

java创建多线程的8种方式(一文搞懂Java多线程)(3)

同样每次输出的结果可能不同,这也是因为子线程执行的进度是不确定的,它们是并发运行的。

Java线程的生命周期

分为六种状态,具体为 创建(New)状态、可运行(Runnable)状态、阻塞(Blocked)状态(不老科特)、等待状态(Waiting)状态、计时等待(Timed waiting)状态、终止(Terminated) 状态。

Java线程调度线程睡眠——Sleep

可以让当前正在执行的线程暂停一段时间

public class www extends JFrame implements Runnable { JLabel jLabel1,jLabel2; public www (){ jLabel1 = new JLabel("当前时间:"); jLabel2 = new JLabel(); Container containerPane = this.getContentPane(); containerPane.setLayout(new FlowLayout()); this.add(jLabel1); this.add(jLabel2); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(300,200); this.setVisible(true); } @Override public void run() { while (true){ jLabel2.setText(getTime()); //获取当前进程 并延时2000ms try { Thread.currentThread().sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } String getTime(){ Date date =new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss Z"); return simpleDateFormat.format(date); } public static void main(String[] args) { www test1 = new www(); Thread thread1 = new Thread(test1); thread1.start(); } }

java创建多线程的8种方式(一文搞懂Java多线程)(4)

可以看出时间每隔2S(2000ms)刷新一次

线程让步——yield(youde)

yield() 和 sleep() 都会暂停执行当前的进程,不同的是 yield() 会先判读是否有比该进程优先级相同或高的进程,若是有则停止让相同或高优先级的先运行,若是没有则该进程继续运行。

public class www implements Runnable { String str = ""; @Override public void run() { for (int i = 1; i <= 9; i ) { str = Thread.currentThread().getName() "----" i " "; if(i%3 == 0){ System.out.println(str); str = ""; Thread.currentThread().yield(); } } } public static void main(String[] args) { www test1 = new www(); www test2 = new www(); Thread thread1 = new Thread(test1,"线程1"); Thread thread2 = new Thread(test2,"线程2"); thread1.start(); thread2.start(); } }

java创建多线程的8种方式(一文搞懂Java多线程)(5)

输出的结果线程是交替的,可能输出的结果不是交替的每次运行都不一样,所以通过yield()控制线程的执行方法是不可靠的。

线程协作——join

若一个线程运行到某一个点的时候,等待另一个线程运行结束后才能继续运行,这种情况可以通过调用另一个线程的join()方法来实现。

public class www { public static void main(String[] args) { Thread thread = new test(); thread.start(); for (int i = 1; i <= 10; i ) { System.out.println("主线程"); if(i == 5){ try { thread.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } } class test extends Thread{ public void run(){ for (int i = 1; i <= 10 ; i ) { System.out.println("子线程"); } } }

java创建多线程的8种方式(一文搞懂Java多线程)(6)

通常情况下,主线程创建并启动了线程,如果子线程运行过程中需要大量的时间,主线程往往早于子线程运行完之前,这个例子是在主线程运行完之前设置状态为 join() 等待子线程运行完再继续运行。

进程的优先级

Java中每个进程都对应的有优先级,优先级高的获得的运行机会多。线程优先级常用1~10之间的数字进行表示,数值越大优先级越高,线程默认等级为5。

Thread类中定义个三个常量

名称

MIN_PRIORITY

1

MAX_PRIORITY

10

NORM_PRIORITY

5

优先级的设定 setPrioriy() 方法 和 获取优先级的方法 getPrioriy()

守护进程

Java分为两类 用户线程 和 守护线程

守护线程也称为后台线程 为其他线程提供服务 这类线程可以监控其他线程的运行 依赖与其他线程

用户线程是一般线程 负责业务逻辑

可通过 setDaemon() 方法来设置一个线程的类型 true为守护线程 只能在 线程.start() 之前调用 还可以通过 isDaemon() 方法来判断该进程是否是守护进程

线程同步

多线程引发的问题:在进行多线程的设计的时候,有时候需要多个线程共享一个部分代码块,从而实现共享一个私有成员变量或类的静态成员的目的。这时由于线程和线程之间相互争夺CPU资源,线程无序地进行访问这些共享资源,最终也可能无法得到正确的结果,这些问题通常称为线程安全问题。

例如:

public class www { public static void main(String[] args) { test test = new test(); for (int i = 0; i < 10; i ) { new Thread(test).start(); } } } class test implements Runnable{ public int num = 0; //使用temp是为了增加线程的切换几率 private void add(){ int temp; for (int i = 0; i < 1000; i ) { temp = num; temp ; num = temp; } System.out.println(Thread.currentThread().getName() " " num); } public void run(){ add(); } }

java创建多线程的8种方式(一文搞懂Java多线程)(7)

输出结果几乎没有正确1000倍数的,这是由于多线程的并发执行,多个线程同时对变量 num 进行修改的结果,解决这个问题就必须要有Java的同步机制。

同步代码块

Java为每个对象配备一把锁和一个等候集,这个对象可以是实例对象,也可以是类对象。对实例对象进行加锁,可以保证这个实例对象相关的线程可以互斥的使用对象锁;对类对象进行加锁可以保证这个类相关的线程可以被互斥的使用类对象的锁。通过new关键字创建实例对象,,从而获得对象的引用,要获得类对象的引用。我们可以通过 forName 成员方法。一个类的静态成员变量和静态成员方法隶属于类对象,而一个类的非静态成员变量和非静态成员方法属于类的实例对象。

synchronize(synObject){ //关键code }

当进程进入关键代码时系统会先检查对象的锁是否被其他线程获取,若没有则JVM把该对象的锁交给当前请求锁的进程,该线程获取锁后即可进入关键代码区域。

例如:

public class www { public static void main(String[] args) { test test = new test(); for (int i = 0; i < 10; i ) { new Thread(test).start(); } } } class test implements Runnable{ int num = 0; private void add(){ int temp; for (int i = 0; i < 1000; i ) { temp = num; temp ; num = temp; } System.out.println(Thread.currentThread().getName() " " num); } public void run(){ synchronized (this){ add(); } } }

java创建多线程的8种方式(一文搞懂Java多线程)(8)

这样下来每个线程执行完毕的结果都是整数了。

将add()方法使用synchronized修饰一样的结果,这就是下面要讲的同步方法

private synchronized void add(){ int temp; for (int i = 0; i < 1000; i ) { temp = num; temp ; num = temp; } System.out.println(Thread.currentThread().getName() " " num); }

同步方法

同步方法和同步代码块一样,都是利用互锁实现的代码同步访问。

public class www { public static void main(String[] args) { test test = new test(); for (int i = 0; i < 10; i ) { new Thread(test).start(); } } } class test implements Runnable{ int num = 0; private synchronized void add(){ int temp; for (int i = 0; i < 1000; i ) { temp = num; temp ; num = temp; } System.out.println(Thread.currentThread().getName() " " num); } public void run(){ add(); } }

java创建多线程的8种方式(一文搞懂Java多线程)(9)

同步是一种高消耗的操作,因此尽量减少用 synchronize 设置大的同步方法,一般情况下使用 synchronize 代码块同步关键代码。

线程通讯

有时多线程会有执行过程中的次序问题,Java提供了三个方法来解决线程的通讯问题。分别是 wait() 、 notify() 、 notifyAll() 方法。这三个关键字只能在synchronized关键字作用范围内起作用,并且是在同一个方法中搭配这三个方法才有实际的意义。

wait() 方法:可以使调用该方法的线程释放共享资源的锁,从可运行状态进入等待状态。知道再次被唤醒。

notify() 方法:可以唤醒等待队列中第一个等待同一共享资源的线程,并使该进程退出等待状态,进入可运行状态。

notifyAll() 方法:可以使所有正在等待队列中等待同一共享资源的进程从等待状态退出,进入可运行状态,若有多个进程,哪个优先级最高先运行哪个。在不知道该唤醒哪个进程的时候使用该方法。

知识点补充:

线程的5种状态详解

下面举个例子:

public class www { public static void main(String[] args) { //实例化一个ShareStore对象 并创建进程启动 ShareStore shareStore = new ShareStore(); new Consumer(shareStore).start(); new Producer(shareStore).start(); } } /*** * 生产者 */ class Producer extends Thread{ private ShareStore shareStore; Producer(ShareStore shareStore){ this.shareStore = shareStore; } @Override public void run() { int num = 1; while (true){ shareStore.setShareNum( num); System.out.println("Producer生产了一个数字" num); //睡眠1s try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } /*** * 消费者 */ class Consumer extends Thread{ private ShareStore shareStore; Consumer(ShareStore shareStore){ this.shareStore = shareStore; } @Override public void run() { int num = 1; while (true){ num = shareStore.getShareNum(); System.out.println("Consumer消费了一个数字" num); //睡眠1s try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } /*** * 用来管理的 */ class ShareStore{ private int num; private boolean writeable = true; public synchronized void setShareNum(int num){ if (!writeable){ try { wait();//等待消费者消费完成 } catch (InterruptedException e) { throw new RuntimeException(e); } } this.num = num; writeable = false; notify(); //通知消费者 生产者已经生产可以消费 } public synchronized int getShareNum(){ if (writeable){ try { wait(); //等待生产者生产出来 } catch (InterruptedException e) { throw new RuntimeException(e); } } writeable = true; notify();//通知可以生产 return this.num; } }

java创建多线程的8种方式(一文搞懂Java多线程)(10)

输出的结果都是生产者先生产一个数字,消费者再消费。

死锁是一种场景:当两个或多个进程形成单项等待的环,每个进程都在相互等待对方的资源释放,都无法执行一直持续下去就形成了死锁。

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页