Java多线程
什么是线程?
进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间),比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间。当用户再次点击左面的IE浏览器,又启动了一个进程,操作系统将为新的进程分配新的独立的地址空间。目前操作系统都支持多进程。
注意:用户每启动一个进程,操作系统就会为该进程分配一个独立的内存空间。请注意是独立的内存空间
线程的五种状态:
-
新建状态(new)
-
就绪状态(Runnable)
-
运行状态(Running)
-
阻塞状态(Blocked)
-
死亡状态(Dead)
创建线程的两种方式:
1、继承Thread类
Java提供了java.lang.Thread类支持多线程编程
public class MyThread extends Thread{
public void run(){
for (int i = 1; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread thread = new MyThread();
thread.start(); //调用start()方法启动线程
thread.run();
}
}
注意:如果直接调用run()方法的话,则是使用主线程运行。
如果调用start()方法的话,则是一条单独的子线程。
2、实现Runnable接口:
-
实现run()方法;
-
编写线程执行体;
-
创建线程对象;
-
调用start()方法启动线程。
public class MyRunnable implements Runnable {
//Runnable中包含run()方法,必须重写run()
public void run() {
// TODO Auto-generated method stub
for (int i = 1; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyRunnable runnable = new MyRunnable();
MyRunnable runnable1 = new MyRunnable();
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable1);
thread.start();
thread1.start();
}
}
这里使用两个对象调用start()方法,则是两条线程交替执行。
结果如下图:
Thread类和Runnable接口的区别:
一、继承Thread类
编写简单,可直接操作线程
适用于单继承
二、实现Runnable接口
避免单继承局限性
便于共享资源
主线程
main()方法即为主线程入口
产生其他子线程的线程
必须最后完成执行,因为它执行各种关闭动作
线程的休眠
在程序中允许一个线程进行暂时休眠,直接使用Thread.sleep()方法即可实现线程的休眠。
sleep()方法会让当前线程休眠(停止执行)millis毫秒,线程由运行中的状态进入不可运行状态,睡眠时间过后会再次进入可运行状态。
注意:调用sleep()方法需处理InterruptedException异常
public class Sleep implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000); //睡眠一秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(i+1+"秒");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread td = new Thread(new Sleep());
td.start();
}
}
结果如下图所示:
线程的强制执行
join()方法使当前线程暂停执行,等待调用该方法的线程结束后再继续执行本线程。
注意:调用join()方法需处理InterruptedException异常
public class JoinDemo implements Runnable{
public void run() {
for (int i = 0; i <10; i++) {
try {
Thread.sleep(10); //增加线程交替执行的几率
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//输出当前线程的信息
System.out.println(Thread.currentThread().getName()+"运行:"+i);
}
}
public static void main(String[] args) {
//创建子线程并启动
Thread td = new Thread(new JoinDemo());
td.start();
for (int i = 0; i < 20; i++) {
if (i == 5) {
try {
td.join(); //阻塞主线程,子线程强制执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行:"+i);
}
}
}
结果如下图所示:
线程的同步
当两个或多个线程需要访问同一资源时,需要以某种顺序来确保该资源某一时刻只能被一个线程使用,这就称为线程同步。
采用线程同步来控制线程的执行有两种方式,即同步代码方法和同步代码块。这两种方式均使用synchronized关键字来实现。
1、同步方法
public class Site implements Runnable{
private int count = 10; //剩余票数
private int num = 0; //买到第几张票
private boolean flag = false; //记录是否售完
public void run() {
// TODO Auto-generated method stub
while(!flag){
sale();
}
}
//同步方法:售票
public synchronized void sale(){
if (count <= 0) {
flag = true;
return;
}
//第一步:修改数据
num++;
count--;
try {
Thread.sleep(500); //模拟网络延迟
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//第二步:显示信息
System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票");
}
}
2、同步代码块
public class Site implements Runnable{
private int count = 10; //剩余票数
private int num = 0; //买到第几张票
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized (this) { //同步代码块
//没有余票时,跳出循环
if (count <= 0) {
break;
}
//第一步:修改数据
num++;
count--;
try{
Thread.sleep(500); //模拟网络延时
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票");
}
}
}
}