线程
应用程序的执行实例 --> 进程 <-- 有独立的内存空间和系统资源
CPU调度和分派的基本单位 --> 线程 <-- 进程中执行运算的最小单位,可完成一个独立的顺序控制流程
多线程
什么是多线程:
1.如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为"多线程"
2.多个线程交替占用CPU资源, 而非真正的并行执行
多线程的好处:
充分利用CPU的资源
简化编程模型
带来良好的用户体验
主线程
Thread类
Java提供了java.lang.Thread类支持多线程编程
主线程
main()方法即为主线程入口
产生其他子线程的线程
必须最后完成执行,因为它执行各种关闭动作
-显示主线程名
public static void main(String[] args){
Thread t = Thread.currentThread();
System.out.println("当前线程是:"+t.getName());
t.setName("MyJavaThread");
System.out.println("当前线程是:"+t.getName());
}
线程的创建和启动
在Java中创建线程的两种方式
继承java.lang.Thread类
实现java.lang.Runnable接口
使用线程的步骤
STEP 1:定义线程
STEP 2:创建线程对象
STEP 3:启动线程
STEP 4:终止线程
继承Thread类创建线程
1.定义MyThread类继承Thread类
2.重写run()方法,编写线程执行体
public class MyThread extends Thread{
//重写run()方法
public void run(){
for(int i = 0;i<100;i++){
system.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
3.创建线程对象,调用start()方法启动线程
public static void main(String{} args){
MyThread thread = new MyThread();
thread.start();//启动线程
}
4.多个线程交替执行,不是真正的"并行"
5.线程每次执行时长由分配的CPU时间片长度决定
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
启动线程是否可以直接调用run()方法?
主线程调用run():只有主线程一条执行路径
主线程调用start():多条执行路径,主线程和子线程并行交替执行
实现Runnable接口创建线程
比较两种创建线程的方式
继承Thread类
1.编写简单,可直接操作线程
2.适用于单继承
实现Runnable接口
1.避免单继承局限性
2.便于共享资源
线程的状态
线程调度
线程调度指按照特定机制为多个线程分配CPU的使用权
方法 | 说明 |
---|---|
void setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long mills) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程 |
boolean isAlive() | 测试线程是否处于活动状态 |
线程休眠
1.让线程暂时睡眠指定时长,线程进入阻塞状态
2.睡眠时间过后线程会再进入可运行状态
public static void sleep(long millis)
3.millis为休眠时长,以毫秒为单位
4.调用sleep()方法需处理InterruptedException异常
线程设置优先级
setPriority(Thread.MAX_PRIORITY);
setPriority(Thread.MIN_PRIORITY);
线程休眠
Thread.sleep(毫秒)
线程的强制运行
1.使当前线程暂停执行,等待其他线程结束后再继续执行本线程
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)
millis:以毫秒为单位的等待时长
nanos:要等待的附加纳秒时长
需处理InterrputedException异常
public static void main(String[] args){
Thread temp = new Thread(new MyThread());
temp.start();
for(int i = 0;i<20;i++){
if(i == 5){
try{
temp.join();//阻塞主线程,子线程强制执行
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"运行:"+i);
}
//省略代码...
}
线程的礼让
1.暂停当前线程,允许其他具有相同优先级的线程获得运行机会
2.该线程处于就绪状态,不转为阻塞状态
public static void yield()
只是提供一种可能,但是不能保证一定会实现礼让
public class MyThread implements Runnable{
public void run(){
for(int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"正在运行:"+i);
if(i == 3){
System.out.println("线程礼让:");
Thread.yield();//当=3时,线程礼让
}
}
}
}
yield() VS join()
yield()
1.暂停当前正在执行的线程对象,并执行其他线程,线程礼让
2.只是提供一种可能,但是不能保证一定会实现礼让
区别
yield():礼让,放手。当前线程处于就绪状态
join():阻塞当前线程,直到其他线程执行完毕,当前线程才进入就绪状态
同步代码块
使用synchronized关键字修饰的代码块
synchronized(syncObject){
//需要同步的代码块
}
syncObject为需同步的对象,通常为this
效果与同步方法相同
public void run(){
while(true){
synchronized(this){//同步代码块
//省略修改数据的代码
//省略显示信息的代码
}
}
}
synchronized就是为当前的线程声明一把锁
多个并发线程访问同一资源的同步代码块时
1.同一时刻只能有一个线程进入synchronized(this)同步代码块
2.当一个线程访问一个synchronized(this)同步代码块时,其他线程synchronized(this)同步代码块同样被锁定
3.当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
多线程共享数据
可能带来的问题:数据不安全
原因:多线程共同操作数据时,引发的冲突(如延迟时,操作未全部完成等)
线程同步:即各线程之间要有个先来后到,不能一窝蜂
线程同步其实是"排队":上锁,排队,一个一个来,不能同时操作
线程安全的类型
查看ArrayList类的add()方法定义
public boolean add(E e){
//集合扩容,确保能新增数据
ensureCapacityInternal(size+1);
//在新增位置存放数据
elementData[size++]=e;
return true;
}
ArrayList类的add()方法为非同步方法
当多个线程向同一个ArrayList对象添加数据时,可能出现数据不一致问题
方法是否同步 | 效率比较 | 适合场景 | |
---|---|---|---|
线程安全 | 是 | 低 | 多线程并发共享资源 |
非线程安全 | 否 | 高 | 单线程 |
为达到安全性和效率的平衡,可以根据实际场景来选择合适的类型
常见类型对比
Hashtable && HashMap
Hashtable
1.继承关系:实现了Map接口,Hashtable继承Dictonary类
2.线程安全,效率较低
3.键和值都不允许为null
HashMap
1.继承关系:实现了Map接口,继承AbstractMap类
2.非线程安全,效率较高
3.键和值都允许为null
StringBuffer && StringBuilder
1.前者线程安全,后者非线程安全
案例
案例一
-测试类
package hw1;
public class Test {
public static void main(String[] args) {
MyThread th = new MyThread();
MyThread2 th2 = new MyThread2();
th.start();
th2.start();
}
}
-线程类1
package hw1;
public class MyThread extends Thread{
@Override
public void run() {
for(int i = 1;i<21;i++) {
System.out.println(i+".你好,来自线程a "+Thread.currentThread().getName());
}
}
}
-线程类2
package hw1;
public class MyThread2 extends Thread{
@Override
public void run() {
for(int i = 1;i<21;i++) {
System.out.println(i+".你好,来自线程b "+Thread.currentThread().getName());
}
}
}
案例二
实现Runnable接口的方式创建线程
需求说明:修改案例一,要求线程类实现Runnable接口的方式创建,并修改测试类
创建Thraed类对象,使用Runnable接口实现类的对象作为构造方法的参数
-测试类
package hw2;
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread th1 = new Thread(myThread);
Thread th2 = new Thread(myThread);
th1.start();
th2.start();
}
}
-线程类
package hw2;
public class MyThread implements Runnable{
@Override
public void run() {
for(int i = 1;i<21;i++) {
System.out.println(i+".你好,来自线程 "+Thread.currentThread().getName());
}
}
}
案例三
-测试类
package hw3;
public class Test {
public static void main(String[] args) {
MyThread th1 = new MyThread();
MyThread2 th2 = new MyThread2();
th1.setName("年轻人");
th2.setName("老年人");
System.out.println("********开始爬山********");
th1.start();
th2.start();
}
}
-线程类1
package hw3;
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"爬完100米!");
}System.out.println(Thread.currentThread().getName()+"到达终点!");
}
}
-线程类2
package hw3;
public class MyThread2 extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"爬完100米!");
}System.out.println(Thread.currentThread().getName()+"到达终点!");
}
}
案例四
-测试类
package hw4;
public class Test {
public static void main(String[] args) {
//MyThread myThread = new MyThread();
Thread th1 =Thread.currentThread();//主线程
// Thread th2 = Thread.currentThread();
System.out.println("******显示默认优先级******");
Thread th2 = new MyThread();
th1.setPriority(5);
// th2.setPriority(5);
// th1.start();
// th2.start();
System.out.println("主线程名: "+th1.getName()+",优先级: "+th1.getPriority());
System.out.println("子主线程名: "+th2.getName()+",优先级: "+th2.getPriority());
System.out.println("******修改默认优先级后******");
th1.setPriority(10);
th2.setPriority(1);
System.out.println("主线程名: "+th1.getName()+",优先级: "+th1.getPriority());
System.out.println("子主线程名: "+th2.getName()+",优先级: "+th2.getPriority());
}
}
-线程类
package hw4;
public class MyThread extends Thread{
@Override
public void run() {
}
}
案例五
-测试类
package hw5;
public class Test {
public static void main(String[] args) {
/**
* 普通号
*/
Thread th1 = Thread.currentThread();
th1.setPriority(th1.currentThread().MIN_PRIORITY);
th1.setName("普通号");
//th1.start();
/**
* 特需号
*/
Thread th2 = new MyThread();
th2.setPriority(th2.currentThread().MAX_PRIORITY);
th2.setName("特需号");
th2.start();
for(int i = 1; i<51;i++) {
System.out.println(th1.getName()+": "+i+"号病人正在看病!");
System.out.println(th2.getName()+": "+i+"号病人正在看病!");
if(i==10) {
try {
th1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
-线程类
package hw5;
public class MyThread extends Thread{
@Override
public void run() {
}
}
案例六
-测试类
package hw6;
public class Test {
public static void main(String[] args) {
Runnable runnable = new RunThread();
Thread run1 = new Thread(runnable,"1");
Thread run2 = new Thread(runnable,"2");
Thread run3 = new Thread(runnable,"3");
Thread run4 = new Thread(runnable,"4");
Thread run5 = new Thread(runnable,"5");
run1.start();
run2.start();
run3.start();
run4.start();
run5.start();
}
}
-线程类
package hw6;
public class RunThread implements Runnable {
private int metres = 1000;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
synchronized (this) {
if(metres <100) {
break;
}
System.out.println(Thread.currentThread().getName()+"号选手拿到了接力棒!");
for(int i = 0 ;i<100;i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"号选手跑了"+i+"米");
}
metres -= 100;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
案例七
-测试类
package hw7;
public class Test {
public static void main(String[] args) {
Runnable runnable = new MyThread();
Thread t1 = new Thread(runnable,"桃跑跑");
Thread t2 = new Thread(runnable,"张票票");
Thread t3 = new Thread(runnable,"黄牛党");
System.out.println("-----------------------------------");
t1.start();
t2.start();
t3.start();
}
}
-线程类
package hw7;
public class MyThread implements Runnable{
private int ticket = 10;
private int num = 0;
private int hnd=0;
public void run() {
while(true) {
synchronized (this) {
if(ticket <= 0) {
break;
}
if(Thread.currentThread().getName().equals("黄牛党")) {
if(hnd == 1) {
return;
}
hnd++;
}
ticket --;
num ++;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余"+ticket+"张票!");
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
package hw7;
public class MyThread implements Runnable{
private int ticket = 10;
private int num = 0;
private int hnd=0;
public void run() {
while(true) {
synchronized (this) {
if(ticket <= 0) {
break;
}
if(Thread.currentThread().getName().equals("黄牛党")) {
if(hnd == 1) {
return;
}
hnd++;
}
ticket --;
num ++;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余"+ticket+"张票!");
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}