首页 / JAVA / Java从零开始学四十四(多线程)
Java从零开始学四十四(多线程)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java从零开始学四十四(多线程),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含13831字,纯文字阅读大概需要20分钟。
内容图文
一、进程与线程
1.1、进程
进程是应用程序的执行实例。
1.2、线程
- 线程依赖进程生存
- 一个进程可以包含多个线程,而一个线程至少要有一个父进程
- 线程可以有自己的堆栈,程序计数器和局部变量。
- 线程与父进程的其他线程共享进程所有的全部资源
- 线程是独立运行,采用抢占方式。
- 一个线程可以创建和删除别外一具线程
- 同一个进程中的多个线程之间可以并发执行
- 线程的调试管理是由进程来完成的。
- 原则:编程时,必须确保线程不会妨碍同一进程的其他线程
1.3、线程分类
- 系统级线程:又称核心级线程,负责管理调度不同进程之间的多个线程,由操作系统直接管理
- 用户级线程:仅存于用户空间,在应用 程序中控制其创建,执行和消亡
1.4、多线程优势
- 改善用户体验
- 提高资源的利用率
二、Java中线程的实现
- 一种是继承Thread类
- 另一种就是实现Runnable接口
使用步骤:
定义一个线程----创建线程的实例--启动线程--终止线程
2.1、继承Thread类
/** * 继承Thread类重写run方法 * */ public class MyThead1 extends Thread { privateint count; @Override publicvoid run() { System.out.println("=======线程启动了======="); while(this.count<100){ count++; } System.out.println("count最终的值:"+count); } publicint getCount() { return count; } publicvoid setCount(int count) { this.count = count; } }
测试类
/** * 测试类 * MyTheadDemo * 继承Thread类创建线程 * 1.继承Thread类 * 2.重写run方法 * 3.实例化线程类对象 * 4.调用start方法启动线程 * * 继承Thread类存在问题 * Java中是单继承的,线程类不能继承其它的类 * * 解决办法:实现Runnable接口 * */ public class MyTheadDemo1 { public static void main(String[] args) { // 实例化线程类对象 MyThead1 myThead1=new MyThead1(); //启动线程 myThead1.start(); /* * start方法的作用 * 该方法公使操作系统初始化一个新的线程 * 由这个新线程来执行线程对象的Run方法 */ } }
2.2、实现Runnable接口(推荐使用,使用接口可以解决Java中单继承的问题)
/** * 实现Runnable接口 * */ public class MyRunnable implements Runnable { privateint count; @Override publicvoid run() { System.out.println("=======线程启动了======="); while(this.count<100){ count++; } System.out.println("count最终的值:"+count); } publicint getCount() { return count; } publicvoid setCount(int count) { this.count = count; } }
测试类
/** * 测试类 * MyRunnable * 实现Runnable接口创建线程 * 1.实现Runnable接口 * 2.实现run方法 * 3.实例化线程类对象 * 4.创建Thread类实例对象,并将实例化的线程对象传入 * 5.调用Thread类实例对象的start方法启动线程 * */ public class MyRunnableDemo { public static void main(String[] args) { // 实例化线程类对象 Runnable myRunnable=new MyRunnable(); Thread thread=new Thread(myRunnable); //或者Thread thread=new Thread(new MyRunnable()); //启动线程 thread.start(); } }
2.3、启动线程
- 继承Thread类启动线程方法:
- 实现Runnable接口启动线程方法:
2.4、Thread类和Runnable接口的区别
/** * 实现Runnable接口 * */publicclass MyRunnable implements Runnable { privateint count; @Override publicvoid run() { count++; System.out.println("count最终的值:"+count); } publicint getCount() { return count; } publicvoid setCount(int count) { this.count = count; } }
测试类
/** * 测试类 * MyRunnable * 实现Runnable接口创建线程 * 1.实现Runnable接口 * 2.实现run方法 * 3.实例化线程类对象 * 4.创建Thread类实例对象,并将实例化的线程对象传入 * 5.调用Thread类实例对象的start方法启动线程 * */publicclass MyRunnableDemo { publicstaticvoid main(String[] args) { //实例化线程类对象 Runnable myRunnable=new MyRunnable(); Thread thread=new Thread(myRunnable); Thread thread2=new Thread(myRunnable); //或者Thread thread=new Thread(new MyRunnable()); //启动线程 System.out.println("线程1启动"); thread.start(); System.out.println("线程2启动"); thread2.start(); } }
结果:
线程1启动 线程2启动 count最终的值:1 count最终的值:2
再看下thread方式
/** * 继承Thread类重写run方法 * */publicclass MyThead1 extends Thread { privateint count; @Override publicvoid run() { count++; System.out.println("count最终的值:"+count); } publicint getCount() { return count; } publicvoid setCount(int count) { this.count = count; } }
/** * 测试类 * MyTheadDemo * 实现Runnable接口创建纯种 * 1.实现Runnable接口 * 2.重写run方法 * 3.实例化线程类对象 * 4.调用start方法启动线程 * * 继承Thread类存在问题 * Java中是单继承的,线程类不能继承其它的类 * * 解决办法:实现Runnable接口 * */publicclass MyTheadDemo1 { publicstaticvoid main(String[] args) { //实例化线程类对象 MyThead1 myThead1=new MyThead1(); MyThead1 myThead2=new MyThead1(); //启动线程 System.out.println("线程1启动"); myThead1.start(); System.out.println("线程2启动"); myThead2.start(); /* * start方法的作用 * 该方法公使操作系统初始化一个新的线程 * 由这个新线程来执行线程对象的Run方法 */ } }
结果:
线程1启动 线程2启动 count最终的值:1 count最终的值:1
三、线程的状态
四、线程的优先级和方法
4.1、线程的优先级
- 默认情况下,一个线程继承其父类的优先级
- 优先级表示为一个整数值
- 优先级越高,执行的机会越大,反之,执行的机会就越小
- 高优先级的线程可以抢占低优先级线程的CPU资源
- 线程的优先级与线程执行的效率没有必然的联系
4.2、线程的方法
No.
|
方法名称
|
类型
|
描述
|
1
|
public Thread(Runnable target)
|
构造
|
接收Runnable接口子类对象,实例化Thread对象
|
2
|
public Thread(Runnable target,String name)
|
构造
|
接收Runnable接口子类对象,实例化Thread对象,并设置线程名称
|
3
|
public Thread(String name)
|
构造
|
实例化Thread对象,并设置线程名称
|
4
|
public static Thread currentThread()
|
普通
|
返回目前正在执行的线程
|
5
|
public final String getName()
|
普通
|
返回线程的名称
|
6
|
public final int getPriority()
|
普通
|
发挥线程的优先级
|
7
|
public boolean isInterrupted()
|
普通
|
判断目前线程是否被中断,如果是,返回true,否则返回false
|
8
|
public final boolean isAlive()
|
普通
|
判断线程是否在活动,如果是,返回true,否则返回false
|
9
|
public final void join() throws InterruptedException
|
普通
|
等待线程死亡
|
10
|
public final synchronized void join(long millis) throws InterruptedException
|
普通
|
等待millis毫秒后,线程死亡
|
11
|
public void run()
|
普通
|
执行线程
|
12
|
public final void setName(String name)
|
普通
|
设定线程名称
|
13
|
public final void setPriority(int newPriority)
|
普通
|
设定线程的优先值
|
14
|
public static void sleep(long millis) throws InterruptedException
|
普通
|
使目前正在执行的线程休眠millis毫秒
|
15
|
public void start()
|
普通
|
开始执行线程
|
16
|
public static void yield()
|
普通
|
将目前正在执行的线程暂停,允许其它线程执行
|
17
|
public final void setDaemon(boolean on)
|
普通
|
将一个线程设置成后台运行
|
18
|
public final void setPriority(int newPriority)
|
普通
|
更改线程的优先级
|
五、线程调度
5.1、join()方法
作用:阻塞指定的线程等到另一个线程完成以后,再继续执行
package thead; /** * 实现join方法 * 1.join的线程运行完成后,才会继承运行当前线程 * 2.join之前要先启动线程start方法 * */ public class JoinRunnable implements Runnable { @Override publicvoid run() { for (int i = 0; i <5; i++) { System.out.println(Thread.currentThread().getName()+" 第"+i+"次"); } } publicstaticvoid main(String[] args) { //主线程for (int i = 0; i <10; i++) { if(i==5){ Thread thread=new Thread(new JoinRunnable());; thread.setName("半路切入的线程:"); try { //启动线程 thread.start(); //半路加入线程 thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+" 第"+i+"次"); } } }
结果:
main 第0次
main 第1次
main 第2次
main 第3次
main 第4次
半路切入的线程: 第0次
半路切入的线程: 第1次
半路切入的线程: 第2次
半路切入的线程: 第3次
半路切入的线程: 第4次
main 第5次
main 第6次
main 第7次
main 第8次
main 第9次
5.2、sleep()
阻塞当前线程,当前等待的线程将获得机会
调用sleep方法后,,当前线程会被扶起(暂停执行)当前线程会释放资源
package thead; public class SleepRunnable implements Runnable { @Override publicvoid run() { try { for (int i = 0; i < 10; i++) { //每一秒执行一次 Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+i); } } catch (InterruptedException e) { e.printStackTrace(); } } publicstaticvoid main(String[] args) { Thread sleepThread=new Thread(new SleepRunnable()); sleepThread.setName("等待中的线程"); sleepThread.start(); for (int i = 0; i < 10; i++) { if(i==5){ try { //主线程=等5秒 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+i); } } }
结果:
main0
main1
main2
main3
main4
等待中的线程0
等待中的线程1
等待中的线程2
等待中的线程3
main5
main6
main7
main8
main9
等待中的线程4
等待中的线程5
等待中的线程6
等待中的线程7
等待中的线程8
等待中的线程9
5.3、yield()方法
将当前线程转入可运行状态,
package yield; /** * 线程一 * */ public class MyThread1 implements Runnable { @Override publicvoid run() { for (int i = 0; i <10; i++) { Thread.yield(); System.out.println(Thread.currentThread().getName()+":"+i); } } } package yield; /** * 线程二 * */publicclass MyThread2 implements Runnable { @Override publicvoid run() { for (int i = 0; i <10; i++) { Thread.yield(); System.out.println(Thread.currentThread().getName()+": "+i); } } } package yield; /** * yield方法使用 *2个线程抢占方式 */publicclass Test { publicstaticvoid main(String[] args) { Thread myThread1=new Thread(new MyThread1()); myThread1.setName("线程1"); Thread myThread2=new Thread(new MyThread2()); myThread2.setName("线程2"); //同时启动2个线程 myThread1.start(); myThread2.start(); } }
结果:
线程2: 0 线程1:0 线程2: 1 线程1:1 线程2: 2 线程1:2 线程2: 3 线程1:3 线程1:4 线程2: 4 线程1:5 线程1:6 线程1:7 线程1:8 线程1:9 线程2: 5 线程2: 6 线程2: 7 线程2: 8 线程2: 9
sleep()和yield()方法比较
- sleep()方法使当前线程转入被阻塞的状态,而yield()方法使用当前线程转入可运行状态
- sleep()方法总是强制当前线程停止执行,百yield() 方法不一定
- sleep()方法可以使其它等待运行的线程有同样的执行级别而yield()方法只使相同或者更高优先级的线程获得执行机会
- 使用sleep()方法时需要捕获异常,而yield()方法无需要捕获异常
5.4、setDaemon()方法
将线程设置 为后台线程(守护线程)。
只能在线程启动之前设置.
package thead; public class Daemon implements Runnable { @Override publicvoid run() { for (int i = 0; i <10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } publicstaticvoid main(String[] args) { Thread daemon=new Thread(new Daemon()); daemon.setName("后台线程"); // 设置为后台线程 daemon.setDaemon(true); daemon.start(); for (int i = 0; i <10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
六、同步
6.1、简单例子
同一账户2个人同时取款
package demo; public class Account { // 余额 private int balance=500; //检查余额publicint getBalance() { return balance; } //取款publicvoid withDraw(int amount){ balance=balance-amount; } publicvoid setBalance(int balance) { this.balance = balance; } } package demo; publicclass TestAccount implements Runnable{ private Account account=new Account(); @Override publicvoid run() { for (int i = 0; i <5; i++) { //一次取走100 makeWithDraw(100); if(account.getBalance()<0){ System.out.println("账户透支了!!!!!"); } } } //取款参数 同步方法privatesynchronizedvoid makeWithDraw(int amount){ if(account.getBalance()>=amount){ //当前余额是否足够可以取款 System.out.println(Thread.currentThread().getName()+" 准备取款"); try { //0.5秒后取款 Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //完成取款 account.withDraw(amount); System.out.println(Thread.currentThread().getName()+" 完成取款,当前余额为: "+account.getBalance()); }else{ //如果余额不足不能取款 System.out.println(Thread.currentThread().getName()+" 余额不足以支付当前取款, 余额为: " +account.getBalance()); } } }
测试类
package demo; /** * 如果一个资源被多个线程使用,不上锁就会造成读取严重错误 * 如果想让当前资源被一个线程使用时,不会受到其他纯种的影响,应该给当前资源上锁 * Java中使用sychronized关键字保证数据同步 * */ public class Test { public static void main(String[] args) { // 创建线程类的实例 TestAccount ta=new TestAccount(); //创建线程 Thread thread1=new Thread(ta); thread1.setName("张三"); Thread thread2=new Thread(ta); thread2.setName("张三的妻子"); //启动线程 thread1.start(); thread2.start(); } }
上面是同步方法
下面是同步代码块
package demo; public class TestAccount implements Runnable{ private Account account=new Account(); @Override publicvoid run() { for (int i = 0; i <5; i++) { //一次取走100 makeWithDraw(100); if(account.getBalance()<0){ System.out.println("账户透支了!!!!!"); } } } //取款参数privatevoid makeWithDraw(int amount){ //同步代码块synchronized (account){ if(account.getBalance()>=amount){ //当前余额是否足够可以取款 System.out.println(Thread.currentThread().getName()+" 准备取款"); try { //0.5秒后取款 Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //完成取款 account.withDraw(amount); System.out.println(Thread.currentThread().getName()+" 完成取款,当前余额为: "+account.getBalance()); }else{ //如果余额不足不能取款 System.out.println(Thread.currentThread().getName()+" 余额不足以支付当前取款, 余额为: " +account.getBalance()); } } } }
七、线程间通信的实现
- wait()方法:
- notify()方法:
- notifyAll()方法:
以上3个方法只能在同步方式或者同步代码块中使用
wait()方法:会挂起当前的线程,并且释放共享资源的锁。当前线程会从可运行状态转为阻塞状态直到调用了wait()方法所属的那个对象的notify()方法或者notifyAll()方法.
notify()方法:可以唤醒因为调用wait()方法而被挂起的那个线程,并且使这个线程退出阻塞状态进入可运行状态
notifyAll()方法:可以唤醒因为所有调用wait()方法而被挂起的那个线程,并且使这些线程退出阻塞状态进入可运行状态
生产者和消费者
package com.pb; /** * 商品共享数据 * */ public class SharedData { private char c; private boolean idProducted=false;//信号量 //同步方法生产者生产产品的的方法publicsynchronizedvoid putSharedChar(char c){ //如果产品还没有被消费,则生产都等待if(idProducted){ try { System.out.println("消费者还未消费,因此生产都停止生产"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.c=c; idProducted=true; //标记已经生产 notify();//通知消费都 System.out.println("生产者生产了产品"+c+",通知消费者"); } //同步方法 消费者消费产品的的方法publicsynchronizedchar getSharedChar(char c){ //如果产品还没有被消费,则生产都等待if(idProducted==false){ try { System.out.println("生产者还未生产,消费者停止消费"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } idProducted=false; //标记已经消费 notify();//通知生产者 System.out.println("消费者消费了产品"+c+",通知生产者"); returnthis.c; } }
package com.pb; /** * 生产者 * */ public class Producer implements Runnable { //共享数据private SharedData sharedData; public Producer(SharedData sharedData){ this.sharedData=sharedData; } @Override publicvoid run() { for (char c = ‘A‘; c <= ‘D‘; c++) { try { Thread.sleep((int)Math.random()*3000); } catch (InterruptedException e) { e.printStackTrace(); } //将产品生产后,放入仓库 sharedData.putSharedChar(c); } } }
package com.pb; /** * 消费者 * */ public class Consumer implements Runnable { //共享数据private SharedData sharedData; public Consumer(SharedData sharedData){ this.sharedData=sharedData; } @Override publicvoid run() { char ch = 0; do { try { Thread.sleep((int)Math.random()*3000); } catch (InterruptedException e) { e.printStackTrace(); } //从仓库中取中产品 ch=sharedData.getSharedChar(ch); } while (ch!=‘D‘); } }
测试类
package com.pb; public class CommunicationDemo { public static void main(String[] args) { // 共享资源,产品 SharedData sharedData=new SharedData(); Thread producer=new Thread(new Producer(sharedData)); Thread consumer=new Thread(new Consumer(sharedData)); //启动线程 consumer.start(); producer.start(); } }
结果:
生产者还未生产,消费者停止消费
生产者生产了产品A,通知消费者
消费者消费了产品A,通知生产者
生产者生产了产品B,通知消费者
生产者还未生产,消费者停止消费
生产者生产了产品C,通知消费者
消费者消费了产品B,通知生产者
生产者生产了产品D,通知消费者
消费者消费了产品C,通知生产者
原文:http://www.cnblogs.com/liunanjava/p/4437229.html
内容总结
以上是互联网集市为您收集整理的Java从零开始学四十四(多线程)全部内容,希望文章能够帮你解决Java从零开始学四十四(多线程)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。