初识java多线程
来源:互联网 发布:c语言不等于号怎么打 编辑:程序博客网 时间:2024/06/05 18:21
一、创建多线程的方法一:
创建java.lang.Thread的子类,重写该类的run方法
基本用法例子:
public class ThreadTest1 { public static void main(String[] args) { // TODO Auto-generated method stub // 1、创建一个线程对象 Thread thr = new FirstThread(); // 2、调用线程对象的start()方法启动线程 thr.start(); String threadName = Thread.currentThread().getName(); for (int i = 0; i < 100; i++){ System.out.println(threadName + ": " + i); } }}//继承Thread类class FirstThread extends Thread{/** 线程体在run()方法中*/public void run(){ // 显示当前线程名称 String threadName = Thread.currentThread().getName(); for (int i = 0; i < 100; i++){ System.out.println(threadName + ": " + i); } }}//输出结果为:main: 0Thread-0: 0Thread-0: 1main: 1main: 2Thread-0: 2main: 3Thread-0: 3Thread-0: 4...0~99 输出了两遍
思考一下如何去实现两个线程共享一个变量?
1. 思路一:是用static静态化一个变量
public class PrintNumber { public static void main(String[] args){ Thread thread1 = new PrintThread("thread1"); Thread thread2 = new PrintThread("thread2"); thread1.start(); thread2.start(); }}class PrintThread extends Thread{ private static int i; public PrintThread(String name){ super(name); } public void run(){ for (i = 0; i < 100; i++){ System.out.println(getName() + ": " + i ); } }}
2.思路二: 传递一个对象给一个进程,通过对象在进程中共享数据
public class PrintNumber { public int i; public static void main(String[] args){ PrintNumber pn1 = new PrintNumber();// 需要传递给进程的对象 Thread thread1 = new PrintThread("thread1", pn1); Thread thread2 = new PrintThread("thread2", pn1); thread1.start(); thread2.start(); }}class PrintThread extends Thread{ PrintNumber pn; public PrintThread(String name, PrintNumber pn){ super(name); this.pn = pn; } public void run(){ for (pn.i = 0; pn.i < 100; pn.i++){ System.out.println(getName() + ": " + pn.i ); } }}
二、创建多线程的方法二:
实现Runnable接口的方式:
1、创建实现Runnable接口的实现类:必须实现run()方法
2、创建第一步那个实现类的实例对象
3、利用Thread(Runnable target)构造器,创建Thread对象
4、调用Thread类的start方法调用线程
例子:
//实现MyRunnbale是Runnable的实现类public class MyRunnable implements Runnable { public static int i = 0; @Override // 实现run方法 public void run() { for (; i < 100; i++){ System.out.println(Thread.currentThread().getName() + ": " + i); } } public static void main(String[] args) { // TODO Auto-generated method stub MyRunnable mr = new MyRunnable(); Thread th1 = new Thread(mr); Thread th2 = new Thread(mr); th1.start(); th2.start(); }}
三、线程的生命周期
线程的生命周期指的是线程从启动,直到结束
可以调用Thread类的相关方法影响线程的运行状态
线程的运行状态有:
- 新建(new)
- 可执行(Runnable)
- 运行(Running)
- 阻塞(Blocking)
- 死亡(Dead)
新建状态
当新建了一个Thread对象时,该进程处于新建状态,没有启动,所以无法执行
可执行状态
其他线程调用了处于新建状态线程的start方法,该线程对象转换到“可执行状态”
线程拥有获得cpu的权利,处于等待调度阶段
运行状态
处于可执行状态的线程一旦获得cpu的控制权,就会转换到运行状态,
在执行状态下,线程状态占用cpu时间片段,执行run方法中的代码,处于执行状态下的线程可以调用yield方法,该方法用于主动让出cpu控制权。线程对象让出控制权后,回到可执行状态,重新等待调度;
阻塞状态
线程在执行状态下,由于某种条件影响,会被迫让出cpu控制权,进入“阻塞状态”
进入阻塞状态有三种情况:
- 调用sleep 方法:
Thread的sleep方法能让线程暂止休眠一段时间,单位是毫秒
实际例子:
public class MyRunnable implements Runnable { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 10; i++){ System.out.println(Thread.currentThread().getName() + ": " + i); try { // Thread的sleep方法能让线程暂止休眠一段时间,单位是毫秒 Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { // TODO Auto-generated method stub MyRunnable mr = new MyRunnable(); Thread th1 = new Thread(mr); th1.start(); }}
- 调用join方法
处于执行状态的调用了其他线程的join方法,将会挂起,进入阻塞状态
目标线程执行完毕后,才能解除阻塞,回到可执行状态;
public class ThreadJoin extends Thread{ public void run(){ for (int i = 0; i < 10; i++){ System.out.println(getName() + ": " + i); } } public static void main(String[] args) { // TODO Auto-generated method stub ThreadJoin thread = new ThreadJoin(); thread.start(); for (int i = 0; i < 10; i++){ System.out.println(Thread.currentThread().getName() + ": " + i); if (i == 5){ try { thread.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }}// 输出结果;main: 0Thread-0: 0main: 1main: 2Thread-0: 1main: 3main: 4Thread-0: 2main: 5Thread-0: 3Thread-0: 4Thread-0: 5Thread-0: 6Thread-0: 7Thread-0: 8Thread-0: 9main: 6main: 7main: 8main: 9
执行I/O操作
解除阻塞状态有三种情况:- 睡眠超时
- 调用join后等待其他线程执行完毕
- I/O操作完毕
- 调用堵塞线程的interrupter方法(线程睡眠时,调用该方法会抛出InterrupterException)
public class Interrupter extends Thread { public static void main(String[] args) { // TODO Auto-generated method stub Interrupter interrupter = new Interrupter(); interrupter.start(); interrupter.interrupt(); //解除线程的堵塞状态 } public void run(){ for (int i = 0; i < 10; i++){ System.out.println(getName() + ": " + i); if (i == 5){ try { Thread.sleep(100000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }}执行结果:Thread-0: 0Thread-0: 1Thread-0: 2Thread-0: 3Thread-0: 4Thread-0: 5java.lang.InterruptedException: sleep interrupted //这是提示运行时异常Thread-0: 6Thread-0: 7Thread-0: 8Thread-0: 9 at java.lang.Thread.sleep(Native Method) at Interrupter.run(Interrupter.java:15)
死亡状态
处于执行状态的线程一旦从run()方法返回,无论是正常退出还是抛出异常,就会进入“死亡状态”;
已经死亡的线程不能重新运行,否则会抛出异常illegalThreadStateException
可以使用Thread类的isAlive方法判断线程是否活着;
四、线程的优先级
- Thread类提供了设置和获取当前优先级的方法:
setPriority();设置优先级不一定起作用,在不同的JVM、操作系统上,效果不同,操作系统不能保证设置了优先级的线程会优先执行或者获得更多的cpu时间
getPriority(); - Java设置了10个优先级,1~10;1最低,10最高,主线程默认优先级为5;
- 三个代表优先级的常量:MIN_PRIORITY、MAX_PRIORITY、NORM_PRIORITY,分别代表1、10、5
public class PriortityTest extends Thread{ public PriortityTest(String name) { // TODO Auto-generated constructor stub super(name); } public static void main(String[] args) { // TODO Auto-generated method stub PriortityTest priThread0 = new PriortityTest("线程一"), priThread1 = new PriortityTest("线程二1"); System.out.println(priThread0.getPriority());// 5 System.out.println(priThread1.getPriority());// 5 priThread0.setPriority(MIN_PRIORITY);//值为10,The maximum priority that a thread can have. priThread1.setPriority(MAX_PRIORITY); priThread0.start(); priThread1.start(); } public void run(){ for (int i = 0; i < 10; i++){ System.out.println(getName() + ": " + i); } }}
五、线程共享以及安全问题:
首先来看个例子:有五个苹果,两个线程同时拿苹果,每拿一个,打印谁拿了,剩下几个; 利用多线程,两个线程共享资源appleCount,这样在其中一个线程拿完一个苹果后,还没来的及打印,第二个线程已经将appCount--;所以会导致打印剩下的苹果时出现错位;
public class ShareApple implements Runnable{ private int appleCount = 5; boolean getApple(){ if (appleCount > 0){ appleCount--; try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "拿走一个苹果," + "剩" + appleCount); return true; } return false; } public void run(){ boolean flag = getApple(); while (flag){ flag = getApple(); } } public static void main(String[] args) { // TODO Auto-generated method stub ShareApple sa = new ShareApple(); Thread th1 = new Thread(sa); Thread th2 = new Thread(sa); th1.setName("shao"); th2.setName("jinghong"); th1.start(); th2.start(); }}//输出结果为:shao拿走一个苹果,剩3jinghong拿走一个苹果,剩3shao拿走一个苹果,剩1jinghong拿走一个苹果,剩1shao拿走一个苹果,剩0
使用synchronized代码块解决线程安全问题
需要在synchronized代码块参照线程共同的对象:
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
synchornized(){ //...}
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
public synchronized void method(){ // ...}
- 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
public synchronized static void method() { // todo}
- 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
class ClassName { public void method() { synchronized(ClassName.class) { // todo } }}
synchronized关键字,确保共享资源只能在同一个时刻被一个线程访问,这种处理机制称为线程同步,或者线程互斥,基于“对象锁”的概念
public class ShareApple implements Runnable{ private int appleCount = 5; boolean getApple(){ synchronized(this){ // this是一个共同的参照对象,这里指的是同一个ShareApple对象 if (appleCount > 0){ appleCount--; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "拿走一个苹果," + "剩" + appleCount); return true; } return false; } } public void run(){ boolean flag = getApple(); while (flag){ flag = getApple(); } } public static void main(String[] args) { // TODO Auto-generated method stub ShareApple sa = new ShareApple(); Thread th1 = new Thread(sa); Thread th2 = new Thread(sa); th2.setName("shao"); th1.setName("jinghong"); th1.start(); th2.start(); }}
线程通信
当一个线程在使用同步方法的某个参数时,而此变量又需要其他线程修改后才能符合本线程的需要,那么可以在线程中添加wait()方法;
相关方法:wait(), notify(), notifyAll();
这些方法必须在同步方法中调用。
例如:有如下情景,刘关张三个买票,售货员只有一张5元,张飞拿20元,刘备、关羽各拿五元,当张飞买票时,售货员会说:”没有足够多的零钱,需要等待后面的人买票后有零钱之后才能把票卖给你”;
public class Ticket implements Runnable{ private int fiveCount = 1, tenCount = 0, twentyCount = 0; public synchronized void buy(){ String name = Thread.currentThread().getName(); if ("张飞".equals(name)){ if (fiveCount < 3){ try { System.out.println("没有足够多的零钱,需要等待有零钱之后才能把票卖给张飞"); wait(); System.out.println("卖给张飞一张票,并给他找零"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } else if ("关羽".equals(name)){ System.out.println("关羽给售票员一张五元"); fiveCount ++; //关羽给售票员一张五元 } else if("刘备".equals(name)){ System.out.println("刘备给售票员一张五元"); fiveCount ++;// 刘备给售票员一张五元 } // 唤醒进程的条件 if (fiveCount == 3){ notifyAll(); } } @Override public void run() { // TODO Auto-generated method stub buy(); } public static void main(String[] args){ Ticket ticket = new Ticket(); Thread th1 = new Thread(ticket); Thread th2 = new Thread(ticket); Thread th3 = new Thread(ticket); th1.setName("张飞"); th2.setName("关羽"); th3.setName("刘备"); th1.start(); th2.start(); th3.start(); }}//输出结果为:没有足够多的零钱,需要等待有零钱之后才能把票卖给张飞刘备给售票员一张五元关羽给售票员一张五元卖给张飞一张票,并给他找零
两个线程如何交替打印a-z
public class TrigglePrint implements Runnable{ char c = 'a'; public synchronized void print(){ System.out.println(Thread.currentThread().getName() + ": " + c); notifyAll(); try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { // TODO Auto-generated method stub for (; c < 'z'; c++){ print(); } } public static void main(String[] args) { // TODO Auto-generated method stub TrigglePrint tp = new TrigglePrint(); Thread thread1 = new Thread(tp); Thread thread2 = new Thread(tp); thread1.start(); thread2.start(); }}//结果:Thread-0: aThread-1: aThread-0: bThread-1: cThread-0: dThread-1: eThread-0: fThread-1: gThread-0: hThread-1: iThread-0: j......
- Java多线程初识
- 初识Java多线程
- 初识Java多线程
- 初识Java多线程编程
- 初识Java多线程
- 初识Java多线程
- 初识java多线程
- Java多线程编程--初识线程
- 初识Java——多线程
- java多线程学习——初识多线程
- Java多线程编程--(1)初识线程
- Java学习经验(一)多线程初识
- Java多线程编程--(1)初识线程
- 初识java多线程的wait(), notify()方法
- 初识java多线程 (java多线程基础知识汇总)
- 初识多线程
- 初识多线程
- 初识多线程
- luogu P1341 无序字母对
- 10.23(周一)
- Android 6.0动态申请权限
- JDBC纯驱动连接MySQL
- BZOJ 1296 [SCOI2009]粉刷匠
- 初识java多线程
- C#+AE加载shape图层
- JDK7中Executors源码概述
- 点菜系统e-r图
- STL学习之string类
- 05Python中的number数据类型
- cvCloneImage()内存泄漏解决方法, cvCloneImage()和cvCopy()的区别
- Jzoj4747 被粉碎的线段树
- js常见报错之Unexpected token in JSON at position