1.多进程与多线程
1.1多进程:
一个进程是一个包含自身地址的程序,每个独立执行的程序都称为进程,也就是正在执行的程序,系统可以分配给每个进程一段有限的使用CPU的时间(CPU时间片),CPU在这个时间段中执行某个进程,然后下一个时间片又会跳至另一个进程中去执行;由于CPU转换很快,所以使得每个进程好像是在同时执行一样。
1.2多线程:
Java语言提供了并发机制,程序中可以执行多个线程,每个线程完成一个功能,并于其他线程并发执行,这种机制被称为多线程。
一个线程则是进程中的执行流程,一个进程中可以同时包括多个线程,每个线程可以得到一下段程序的执行时间,这样一个进程就可以具有多个并发执行的线程。
2.实现线程的2种方式
2.1 继承Thread类
继承java.lang.Thread类,从这个类中实例化的对象代表线程,启动一个新线程需要建立一个Thread实例。构造方法:
2.1.1 public Thread()
创建一个新的线程对象。
2.1.2 public Thread(String threadName)
创建一个名为threadName的线程对象。
public class ThreadTest extends Thread{
//
}
2.1.3 run()方法:
完成线程真正功能的代码凡在类的run()方法中,当一个类继承Thread类后,就可以重写覆盖run()方法;
run()方法必须使用一下语法:
public void run(){
//
}
2.1.4 start()方法:
调用Thread类中的strat()方法启动执行线程,start()方法会调用run()方法。
public static void main(String[] args){
new ThreadTest().start();
}
主方法main()线程的启动是由Java虚拟机负责的,我们只需要负责自动自己定义的线程即可。
package threadwork;
public class CountThread extends Thread { //指定继承Thread类
private int count = 10;
public void run() { //重写run()方法
while (true) {
System.out.print(count + " ");
if (--count == 0) { //count自减
return;
}
}
}
public static void main(String[] args) {
CountThread ct = new CountThread(); //实例化线程对象
ct.start(); //启动线程
}
}
输出:
10 9 8 7 6 5 4 3 2 1
2.2 实现Runable接口
Thread 是类,而Runnable是接口;Thread本身是实现了Runnable接口的类。我们知道“一个类只能有一个父类,但是却能实现多个接口”,因此Runnable具有更好的扩展性;如果我们定义的类相想要继承其他类(非Thread类),并且还要当前定义的类实现多线程,可以通过Runnable接口来实现。
此外,Runnable还可以用于“资源的共享”。即,多个线程都是基于某一个Runnable对象建立的,它们会共享Runnable对象上的资源。
实际上,Thread类就是实现了Runnable接口,Thread类中的run()方法也是对Runnable接口中的run()方法的实现。
2.2.1 语法:
public class MyDemo extends Object implents Runable{
//
}
2.2.2 继承Thread类实现多线程
package threadwork;
class Ticket extends Thread {
public void run() {
for (int ticket = 10; ticket > 0; ticket--) {
System.out.println(this.getName() + " 买票:ticket" + ticket);
}
}
}
public class TicketThreadTest {
public static void main(String[] args) {
Ticket tt_1 = new Ticket();
Ticket tt_2 = new Ticket();
Ticket tt_3 = new Ticket();
tt_1.start();
tt_2.start();
tt_3.start();
}
}
输出:
Thread-0 买票:ticket10
Thread-2 买票:ticket10
Thread-1 买票:ticket10
Thread-2 买票:ticket9
Thread-2 买票:ticket8
Thread-2 买票:ticket7
Thread-2 买票:ticket6
Thread-0 买票:ticket9
Thread-0 买票:ticket8
Thread-2 买票:ticket5
Thread-2 买票:ticket4
Thread-1 买票:ticket9
Thread-2 买票:ticket3
Thread-0 买票:ticket7
Thread-2 买票:ticket2
Thread-1 买票:ticket8
Thread-1 买票:ticket7
Thread-1 买票:ticket6
Thread-2 买票:ticket1
Thread-0 买票:ticket6
Thread-0 买票:ticket5
Thread-1 买票:ticket5
Thread-1 买票:ticket4
Thread-0 买票:ticket4
Thread-0 买票:ticket3
Thread-1 买票:ticket3
Thread-0 买票:ticket2
Thread-1 买票:ticket2
Thread-1 买票:ticket1
Thread-0 买票:ticket1
2.2.3 实现Runnale接口的多线程
使用Runnable接口启动新的线程步骤如下:
a.定义的类实现Runnable接口;
b.定义的类中实现run()方法;
c.实例化建立Runnable对象(自定义类的对象);
d.使用参数为Runnable对象的构造方法创建Thread实例;
e.调用start()方法启动线程;
package threadwork;
class TicketR implements Runnable {
public void run() {
for (int ticket = 0; ticket <= 10; ticket++) {
System.out.println(Thread.currentThread().getName() + " 买票:ticket" + ticket);
}
}
}
public class TicketRunable {
public static void main(String[] args) {
TicketR ticket_R = new TicketR();
Thread t1 = new Thread(ticket_R);
Thread t2 = new Thread(ticket_R);
Thread t3 = new Thread(ticket_R);
t1.start();
t2.start();
t3.start();
}
}
输出:
Thread-1 买票:ticket0
Thread-1 买票:ticket1
Thread-1 买票:ticket2
Thread-1 买票:ticket3
Thread-1 买票:ticket4
Thread-1 买票:ticket5
Thread-1 买票:ticket6
Thread-1 买票:ticket7
Thread-0 买票:ticket0
Thread-0 买票:ticket1
Thread-0 买票:ticket2
Thread-2 买票:ticket0
Thread-0 买票:ticket3
Thread-0 买票:ticket4
Thread-0 买票:ticket5
Thread-1 买票:ticket8
Thread-1 买票:ticket9
Thread-1 买票:ticket10
Thread-0 买票:ticket6
Thread-2 买票:ticket1
Thread-2 买票:ticket2
Thread-2 买票:ticket3
Thread-2 买票:ticket4
Thread-2 买票:ticket5
Thread-0 买票:ticket7
Thread-2 买票:ticket6
Thread-2 买票:ticket7
Thread-2 买票:ticket8
Thread-2 买票:ticket9
Thread-2 买票:ticket10
Thread-0 买票:ticket8
Thread-0 买票:ticket9
Thread-0 买票:ticket10
3.线程的生命周期
虽然多线程看起来像同时执行,但事实上单个CPU在同一时间点上只有一个线程在被执行,只是线程之间切换较快,才有同时执行的假象。
3.1 线程具有7种声明周期:
1.出生状态、2.就绪状态、3.运行状态、4.等待状态、5.休眠状态、6.阻塞状态、7.死亡状态。
当线程的run()方法执行完毕时,线程会进入死亡状态。
3.2 使线程处于就绪状态的方法:
3.2.1调用sleep()方法:
当线程调用Thread类中的sleep()方法时,则会进入休眠状态;
3.2.2 调用wait()方法:
当处于运行状态下的线程调用Thread类中的wait()方法时,该线程就会进入等待状态。
3.2.3 等待输入/输出完成;
当运行中线程在运行状态下发出I/O请求,该线程会进入阻塞状态,I/O结束时线程会进入就绪状态,对于阻塞的线程来说,既是系统资源空闲,线程依然不能回到运行状态。
3.3 使线程从就绪状态-->运行状态的方法:
3.3.1 线程调用notify()方法:
进入等待状态的线程必须调用Thread类中的notify()方法才能被唤醒;
3.3.2 线程调用notifyAll()方法:
将所有处于等待状态下的线程唤醒;
3.3.3 线程调用interrupt()方法:
3.3.4 线程的休眠时间结束;
3.3.5 输入/输出结束;
4.操作线程的方法
4.1 线程的休眠:sleep()方法
sleep()方法参数为毫秒为单位的时间,同时在run()方法中循环被使用。
try{
Thread.sleep(2000); //线程休眠等待2秒
}catch(InterruptException e){
e.printStackTrace();
}
上述代码使线程在2S之内不会进入就绪状态;虽然使用了sleep()方法的线程在一段时间内会醒来,但是并不能保证醒来后它就会进入运行状态,只能保证它进入就绪状态。
4.2 线程的加入:join()方法
当一个线程使用join()方法加入另外一个线程时,另外一个线程会等待这个加入的线程执行完毕后再继续执行;
通常用于在main()主线程内,等待其它子线程完成再接着执行main()主线程;
4.3 线程的中断:布尔型标记控制
以前会使用stop()方法来停止线程,但是JDK不推荐使用此方法,提倡在run()方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止。
public class InterruptedTest implements Runnable{
private boolean isContine = false; //设置一个标记变量,默认设置为false
public void run(){ //重写run()方法
while(true){
//dosomething
if (isContinue){ //当isContinue变为true时,停止线程
break;
}
}
}
public void setContinue(){
this.isContinue = true; //定义设置变量isContinue的方法
}
}
4.4 线程的礼让:yield()方法
Thread类中提供了礼让方法yield(),给当前正在运行状态的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅仅是提醒,没有任何机制保证当前线程会礼让资源;
5.线程的优先级
每个线程都具有各自的优先级,操作系统会根据线程的优先级来决定首先使哪个线程进入运行状态。
5.1 Thread类中成员变量代表了常用优先级:
5.1.1 Thread.MIN_PRIORITY(常数1)
5.1.2 Thread.MAX_PRIORITY(常数10)
5.1.3 Thread.NORM_PRIORITY(常数5)
在默认情况下,优先级都是Thread.NORM_PRIORITY,且每个新产生的线程都会继承父线程的优先级。
5.2 调整线程优先级:setPriority()方法
setPriority()方法的参数为1~10的数值,数值越大,代表优先级越高;
先设置优先级,再start()启动;
package threadwork;
class TicketR implements Runnable {
public void run() {
for (int ticket = 0; ticket <= 2; ticket++) {
System.out.println(Thread.currentThread().getName() + " 买票:ticket" + ticket);
}
}
}
public class TicketRunable {
public static void main(String[] args) throws InterruptedException {
TicketR ticket_R = new TicketR();
Thread t0 = new Thread(ticket_R);
Thread t1 = new Thread(ticket_R);
Thread t2 = new Thread(ticket_R);
t0.setPriority(1); //先设置优先级,再start()启动
t0.start();
t0.join(); //主线程等待t1线程执行完毕
t1.setPriority(4);
t1.start();
t1.join(); //主线程等待t2线程执行完毕
t2.setPriority(10);
t2.start();
t2.join(); //主线程等待t3线程执行完毕
System.out.println("主线程执行结束");
}
}
6.线程的同步
Java提供线程同步机制来防止资源访问的冲突。
6.1 线程安全
问题示例:
package threadwork;
import com.sun.xml.internal.ws.api.ha.StickyFeature;
public class ThreadSafeTest implements Runnable {
private int tikets = 10;
@Override
public void run() {
while (true) {
if (tikets > 0) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
tikets--;
System.out.println(Thread.currentThread().getName() + " 抢票后剩余票:" + tikets + " 张");
}
}
}
public static void main(String[] args) throws InterruptedException {
ThreadSafeTest tst = new ThreadSafeTest();
Thread t0 = new Thread(tst);
Thread t1 = new Thread(tst);
Thread t2 = new Thread(tst);
t0.start();
t1.start();
t2.start();
}
}
输出:
Thread-2 抢票后剩余票:8 张
Thread-0 抢票后剩余票:8 张
Thread-1 抢票后剩余票:8 张
Thread-1 抢票后剩余票:6 张
Thread-0 抢票后剩余票:6 张
Thread-2 抢票后剩余票:5 张
Thread-1 抢票后剩余票:2 张
Thread-0 抢票后剩余票:2 张
Thread-2 抢票后剩余票:2 张
Thread-1 抢票后剩余票:1 张
Thread-0 抢票后剩余票:0 张
Thread-2 抢票后剩余票:-1 张
Thread-1 抢票后剩余票:-2 张
6.2 线程同步机制:synchronized关键字
给共享资源上一道锁, 给定时间只允许一个线程访问共享资源。
6.2.1 同步块
语法:
synchronized(Object){
//
}
通常将共享资源的操作放在synchronized定义的区域内,这样当其他线程也想获取到这个锁时,必须等到锁被释放时才能进入到这个区域。
package threadwork;
public class ThreadSafeTest implements Runnable {
private int tikets = 10;
@Override
public void run() {
while (true) {
synchronized ("") {
if (tikets > 0) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
tikets--;
System.out.println(Thread.currentThread().getName() + " 抢票后剩余票:" + tikets + " 张");
}
}
}
}
public static void main(String[] args) throws InterruptedException {
ThreadSafeTest tst = new ThreadSafeTest();
Thread t0 = new Thread(tst);
Thread t1 = new Thread(tst);
Thread t2 = new Thread(tst);
t0.start();
t1.start();
t2.start();
}
}
输出:
Thread-0 抢票后剩余票:9 张
Thread-0 抢票后剩余票:8 张
Thread-0 抢票后剩余票:7 张
Thread-0 抢票后剩余票:6 张
Thread-0 抢票后剩余票:5 张
Thread-0 抢票后剩余票:4 张
Thread-0 抢票后剩余票:3 张
Thread-0 抢票后剩余票:2 张
Thread-0 抢票后剩余票:1 张
Thread-0 抢票后剩余票:0 张
6.2.2 同步方法
语法:
synchronized void funName(){
//
}
package threadwork;
public class ThreadSafeTest implements Runnable {
private int tikets = 10;
@Override
public synchronized void run() {
while (true) {
if (tikets > 0) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
tikets--;
System.out.println(Thread.currentThread().getName() + " 抢票后剩余票:" + tikets + " 张");
}
}
}
public static void main(String[] args) throws InterruptedException {
ThreadSafeTest tst = new ThreadSafeTest();
Thread t0 = new Thread(tst);
Thread t1 = new Thread(tst);
Thread t2 = new Thread(tst);
t0.start();
t1.start();
t2.start();
}
}
输出:
Thread-0 抢票后剩余票:9 张
Thread-0 抢票后剩余票:8 张
Thread-0 抢票后剩余票:7 张
Thread-0 抢票后剩余票:6 张
Thread-0 抢票后剩余票:5 张
Thread-0 抢票后剩余票:4 张
Thread-0 抢票后剩余票:3 张
Thread-0 抢票后剩余票:2 张
Thread-0 抢票后剩余票:1 张
Thread-0 抢票后剩余票:0 张