java基础之多线程
java基础之多线程
目录
1. 多线程概述
1.1 线程与进程
进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且每个进程都具有一定独立功能。
线程:线程是进程中的一个执行单元,来完成进程中的某个功能。负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
1.2 程序运行原理
- 分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。 - 抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度
2. 线程的创建和启动
2.1 Thread类
继承Thread类创建线程
- 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程要完成的任务。因此把run()方法称为线程执行体。
- 创建Thread子类的实例,即创建线程对象。
- 调用线程对象的start()方法来启动线程。
public class ThreadTest extends Thread{
private int i;
public void run() {
for(i=0;i<50;i++) {
//当线程继承Thread类的时候,直接调用this即可获取当前的进程
//Thread类的getName()方法会返回线程的名字
System.out.println(getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i =0;i<50;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==10) {
//创建第一个线程
new ThreadTest().start();
//创建第二个线程
new ThreadTest().start();
}
}
}
}
2.2 Runnable接口
实现Runnable接口创建线程类
- 定义Runnable接口的实现类,并重写run()方法。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread方法。
- 调用线程的start()方法来启动对象。
public class RunnableTest implements Runnable {
private int i;
@Override
public void run() {
for(i=0;i<50;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i =0;i<50;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==10) {
RunnableTest rs = new RunnableTest();
new Thread(rs).start();
new Thread(rs).start();
}
}
}
}
实现Runnable的原理和好处
- 程序设计遵循的原则:开闭原则,对修改关闭,对扩展开放,减少线程本身和任务之间的耦合性。
- 实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。
- 继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,又有线程任务。
- 实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
2.3 匿名内部类
使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
- 方式1:创建线程对象时,直接重写Thread类中的run方法
new Thread() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + " " + i);
}
}
}.start();
- 方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}).start();
2.4 多线程常用方法
- 获取名字
通过getName()方法获取线程对象的名字 - 设置名字
- 通过构造函数可以传入String类型的名字
new Thread("yyy") { public void run() { //do something... } }.start();
- 通过setName(String)方法可以设置线程对象的名字
new Thread() { public void run() { this.setName("abc"); for(int i = 0; i < 100; i++) { System.out.println(this.getName() + "....bb"); } } }.start();
- 获取当前线程对象
通过Thread.currentThread(), 主线程也可以获取 - 休眠线程
Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒 - 守护线程
setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
Thread t1 = new Thread() {
public void run() {
for(int i = 0; i < 50; i++) {
System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread() {
public void run() {
for(int i = 0; i < 2; i++) {
System.out.println(getName() + "...bb");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.setDaemon(true); //将t1设置为守护线程
t1.start();
t2.start();
//运行结果
Thread-1...bb
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
Thread-1...bb
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
- 加入线程
- join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
- join(int), 可以等待指定的毫秒之后继续
final Thread t1 = new Thread() { public void run() { for(int i = 0; i < 100; i++) { System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa"); } } }; Thread t2 = new Thread() { public void run() { for(int i = 0; i < 100; i++) { System.out.println(getName() + "...bb"); try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } }; t1.start(); t2.start();
2.5 同步代码块和同步方法
- 同步代码块
当多线程并发,有多段代码同时执行时,我们希望某一段代码执行的过程中CPU不要切换到其他线程工作,这时就需要同步。使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块。多个同步代码块如果使用相同的锁对象,那么他们就是同步的。如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码。
public class ThreadT {
public void print1() {
synchronized (ThreadT.class) {
System.out.print("1");
System.out.print("2");
System.out.print("3");
System.out.print("\r\n");
}
}
public void print2() {
synchronized (ThreadT.class) {
System.out.print("a");
System.out.print("b");
System.out.print("c");
System.out.print("\r\n");
}
}
public static void main(String[] args) {
ThreadT tT = new ThreadT();
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
tT.print1();
}
}
};
Thread t2 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
tT.print2();
}
}
};
t1.start();
t2.start();
}
}
- 同步方法
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的。
public class ThreadT {
public synchronized void print1() {
System.out.print(Thread.currentThread().getName()+"----");
System.out.print("1");
System.out.print("2");
System.out.print("3");
System.out.print("\r\n");
}
public synchronized void print2() {
System.out.print(Thread.currentThread().getName()+"----");
System.out.print("a");
System.out.print("b");
System.out.print("c");
System.out.print("\r\n");
}
public static void main(String[] args) {
ThreadT tT = new ThreadT();
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 1000; i++) {
tT.print1();
}
}
};
Thread t2 = new Thread() {
public void run() {
for (int i = 0; i < 1000; i++) {
tT.print2();
}
}
};
t1.start();
t2.start();
}
}
//输出结果(一部分)
Thread-0----123
Thread-0----123
Thread-1----abc
Thread-1----abc
Thread-1----abc
Thread-0----123
Thread-0----123
2.6 线程间通信
多个线程并发执行时,在默认情况下CPU是随机切换线程的。如果我们希望他们有规律的执行,就可以使用通信,例如每个线程执行一次打印。
如果希望线程等待,就调用wait(),如果希望唤醒等待的线程,就调用notify();这两个方法必须在同步代码中执行,并且使用同步锁对象来调用。notifyAll()方法是唤醒所有线程,JDK5之前无法唤醒指定的一个线程,如果多个线程之间通信,需要使用notifyAll()通知所有线程,用while来反复判断条件。
public class ThreadT {
private int flag = 1;
public synchronized void print1() {
try {
/*
if (flag != 1) { //if 语句是在那里等待就在那里起来
this.wait();
}*/
while(flag != 1){
this.wait(); //while循环是循环判断,每次都会判断标记
}
System.out.print("1");
System.out.print("2");
System.out.print("3");
System.out.print("\r\n");
flag = 2;
//this.notify();
this.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void print2() {
try {
while(flag != 2){
this.wait();
}
System.out.print("a");
System.out.print("b");
System.out.print("c");
System.out.print("\r\n");
flag = 3;
this.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void print3() {
try {
while(flag != 3){
this.wait();
}
System.out.print("+");
System.out.print("-");
System.out.print("*");
System.out.print("\r\n");
flag = 1;
this.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ThreadT tT = new ThreadT();
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
tT.print1();
}
}
};
Thread t2 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
tT.print2();
}
}
};
Thread t3 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
tT.print3();
}
}
};
t1.start();
t2.start();
t3.start();
}
}
//输出结果(一部分)
......
abc
+-*
123
abc
+-*
123
......
2.7 互斥锁
使用ReentrantLock类的lock()和unlock()方法进行同步。使用Condition的await()和signal()来暂停和唤醒线程
public class ThreadT {
private int flag = 1;
private ReentrantLock rlock = new ReentrantLock();
private Condition c1 = rlock.newCondition();
private Condition c2 = rlock.newCondition();
private Condition c3 = rlock.newCondition();
public void print1() {
rlock.lock(); //获得锁
try {
while (flag != 1) {
c1.await(); //使当前线程等待,直到发出信号或中断
}
System.out.print("1");
System.out.print("2");
System.out.print("3");
System.out.print("\r\n");
flag = 2;
c2.signal(); //唤醒等待线程。
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rlock.unlock(); //释放锁
}
}
public void print2() {
rlock.lock();
try {
while (flag != 2) {
c2.await();
}
System.out.print("a");
System.out.print("b");
System.out.print("c");
System.out.print("\r\n");
flag = 3;
c3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rlock.unlock();
}
}
public void print3() {
rlock.lock();
try {
while (flag != 3) {
c3.await();
}
System.out.print("+");
System.out.print("-");
System.out.print("*");
System.out.print("\r\n");
flag = 1;
c1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rlock.unlock();
}
}
public static void main(String[] args) {
ThreadT tT = new ThreadT();
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
tT.print1();
}
}
};
Thread t2 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
tT.print2();
}
}
};
Thread t3 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
tT.print3();
}
}
};
t1.start();
t2.start();
t3.start();
}
}