概念
要学习线程,首先要理解三个概念。什么是程序,什么是进程,什么是线程。程序,是指令的集合。进程是正在执行的程序,是静态概念。线程,是进程中一个“单一的连续控制流程”,也称为轻量级进程。、
线程有一下几个点:
1. 一个进程可以拥有多个线程
2. 一个进程中的线程个共享相同的内存单元,即拥有相同的变两个对象,而且他们从同一个堆中分配对象、通信、数据交换等。
3. 由于线程的通信在同一地址,所以通信更简便,信息传送速度也更快。
多线程的实现:
多线程的实现有两种方法:
1.集成Thread类重写run()方法。
2.实现Runnable接口重写run()方法。
例子如下:
public class ThreadDemo {
int i;
public static void main(String[] args) throws Exception {
// 计算偶数的线程,第一种方式,继承Thread
Counteven ce = new Counteven();
ce.start();
Thread.sleep(50);
// 计算奇数的线程,第二种方法,实现Runnable接口
Countodd ct = new Countodd();
Thread th = new Thread(ct);
th.start();
}
}
class Counteven extends Thread {
@Override
public void run() {
for (int j = 2; j <= 20; j += 2) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("2: " + j);
}
}
}
class Countodd implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 20; i += 2) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("1: " + i);
}
}
}
这两种方式各有优缺点。第一种方式直接继承Thread,可以避免多线程共享资源引发的并发问题。但是每开启一个线程都需要new一个新的线程对象。第二种方式只需要一个new线程对象,之后可以通过共享这一个线程对象实现开启多个线程。就如代码中new Thread(ct)中的“ct”。由于资源共享,线程之间通信更加便捷,信息传送速度也更快,但与此同时单个线程对对象的更改对其他线程来说很容易引发一些问题。
多线程的五个状态
新生状态:线程刚被new出来的状态;
就绪状态:线程对象调用start之后,或者由阻塞状态被唤醒或者自己醒来后,程等待cpu调度的状态;
运行状态:线程获得cpu调度之后的状态,在此状态下可以调用yield方法(使当前线程暂时等待一下)使线程重新回到就绪状态。
阻塞状态:当运行状态的线程遇到导致阻塞状态的事件(例如wait、sleep和join等)会使线程放弃cpu资源进入阻塞状态。其中wait是使当前线程等待,直到有其他线程调用notify唤醒;sleep睡眠制定时间,自动“醒来”;join是当其他线程调用join方法“插队”迫使当前线程等待,直到join线程完成才会进入就绪状态。
死亡状态:线程运行至结束的状态。
需要注意的是,线程由运行状态被阻塞而进入阻塞状态后,无论是被唤醒还是自己醒来都会重新进入就绪状态等待cpu调度。只有线程调用yield方法才会由运行状态直接回到就绪状态。
线程同步
这里的同步,我的理解是不是指同一个步子,一起怎么样,而是一个一个来做事情。实现线程同步使用synchronized关键字。有两种实现方法:
1.同步代码块。
在方法中使用synchronized关键字“锁”住某一对象其他线程只有拿到被锁对象的锁才能进行操作。
2.同步方法
直接用关键字synchronized修饰方法,使方法每次只能有一个线程访问,也就是线程都必须拿到方法的锁才能访问方法,访问时独占方法,其他线程不可访问。
同步方法实现交替打印:
public class Print {
private int index = 1;
public static void main(String[] args) throws InterruptedException {
Print p = new Print();// 同一个打印机
PZ pz = new PZ(p);
PI pi = new PI(p);
Thread th1 = new Thread(pi, "数字线程");
Thread th2 = new Thread(pz, "字母线程");
th1.start();
th2.start();
}
// 打印字母方法
public synchronized void print(char c) {
while (index % 3 != 0) {//不是第三个等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(c);
index++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
};
notifyAll();
}
// 打印数字方法
public synchronized void print(int i) {
while (index % 3 == 0) {//第三个等
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(i+" ");
index++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
};
notifyAll();
}
}
class PZ implements Runnable {
private Print p;
/**
* @param p2
*/
public PZ(Print p2) {
this.p = p2;
}
// 打印字母
@Override
public synchronized void run() {
for (char c = 'A'; c <= 'Z'; c++) {
p.print(c);
}
}
}
// 打印数字
class PI implements Runnable {
private Print p;
public PI(Print p1) {
this.p = p1;
}
@Override
public synchronized void run() {
for (int i = 1; i < 53; i++) {
p.print(i);
}
}
}
同步代码块实现对象上锁:
/**
* 1.设计一个多线程的程序如下:设计一个火车售票模拟程序。 假如火车站要有100张火车票要卖出,现在有5个售票点同时售票,
* 用5个线程模拟这5个售票点的售票情况。
*
*/
public class Ticket implements Runnable {
Integer num = 100;
public static void main(String[] args) {
Ticket t = new Ticket();
Thread th1 = new Thread(t, "窗口1");
Thread th2 = new Thread(t, "窗口2");
Thread th3 = new Thread(t, "窗口3");
Thread th4 = new Thread(t, "窗口4");
Thread th5 = new Thread(t, "窗口5");
th1.start();
th2.start();
th3.start();
th4.start();
th5.start();
}
public void run() {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//同步代码块
synchronized (num) {
Thread th = Thread.currentThread();
String name = th.getName();
while (true) {
if (num > 0) {
System.out.println(name + "卖了第" + num + "张票");
num--;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
使用同步能够避免资源共享带来的并发问题。同步代码块效率高但是由于只锁住了代码块,方法还是会有多个线程访问,所以不如同步方法安全,但是同步方法只能一个一个访问方法,导致效率低下。
这就是多线程学习的一些理解,与大家分享。