多线程
程序:是一个指令的集合。
进程:正在执行中的程序,是一个静态的概念。
线程:是进程中的一个单一的连续控制流程,线程又本称为轻量级进程。
一个进程可拥有多个并行的线程,一个进程中的线程共享相同的内存单元,内存地址空间,可以访问相同的变量和对象,而且他们从同一堆中分配对象,通信,数据交换,同步操作。
由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便,而且信息传递速度也更快。
java多线程实现的方法有两种。
第一种:
继承Thread类,重写run方法,创建对象,调用start方法启动多线程。
第二种:
实现Runnable接口,重写run方法,创建对象,调用start方法启动多线程。
实现Runnable接口的类不能直接调用start方法,需要创建一个Thread对象,然后把对象传进去,在调用start方法。
继承Thread类开启多线程:
注意一点,在开启多线程的时候需要调用start,不能直接调用run方法,因为在开启多线程前,需要一些准备工作,这些工作就需要start来做,start在做完这些准备工作后在去调用run方法。
/**
* 使用多线程打印到1-100
*
* @author FengYuan
*
*/
public class Test extends Thread {
/*
* 继承Thread类然后重写run方法
*
* @see java.lang.Thread#run()
*/
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println("线程:" + i);
}
}
public static void main(String[] args) {
Test t = new Test();
t.start();// 注意多线程的开启需要调用start,不能直接调用run。
for (int i = 0; i <= 100; i++) {
System.out.println("main方法::" + i);
}
}
}
实现Runnable接口开启多线程
实现Runnable接口不能直接调用start方法,因为start方法属于Thread的,所以需要new一个Thread然后把对象传进去,通过Thread调用start方法
/**
* 使用多线程打印到1-100
*
* @author FengYuan
*
*/
public class Test implements Runnable {
/*
* 实现Runnable接口然后重写run方法
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println("线程:" + i);
}
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t).start(); // 注意实现Runnable接口对象不可以直接调用start。
for (int i = 0; i <= 100; i++) {
System.out.println("main方法::" + i);
}
}
}
常见的方法:
sleep 睡眠 让线程睡眠一定的时间。它有两种睡眠时间,一种是毫秒,一种是纳秒
/**
* 多线程
*
* @author FengYuan
*
*/
public class Test extends Thread {
/*
* 继承Thread类然后重写run方法
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
sleep(1000); // 让线程睡眠1秒,1000毫秒等于一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i <= 100; i++) {
try {
sleep(0, 1000); // 每打印一次让线程睡眠1000纳秒,当然速度太快,看不出效果。
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + i);
}
}
public static void main(String[] args) {
Test t = new Test();
t.start(); // 注意实现Runnable接口对象不可以直接调用start。
for (int i = 0; i <= 100; i++) {
System.out.println("main方法::" + i);
}
}
}
join插队 任何一个线程在调用join后,那么他就有优先执行的权利,其他的线程都要等到调用join方法的线程执行完毕,才可以执行。
通过上面的代码可以看出run方法在执行的是后遇到了sleep所以睡眠了,这是main继续执行完毕,但是在调用join后main方法就只有等到run方法执行完毕才能继续执行
/**
* 多线程
*
* @author FengYuan
*
*/
public class Test extends Thread {
/*
* 继承Thread类然后重写run方法
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
sleep(1000); // 让线程睡眠1秒,1000毫秒等于一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i <= 100; i++) {
try {
sleep(0, 1000); // 每打印一次让线程睡眠1000纳秒,当然速度太快,看不出效果。
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + i);
}
}
public static void main(String[] args) {
Test t = new Test();
t.start(); // 注意实现Runnable接口对象不可以直接调用start。
try {
t.join(); // 调用join
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i <= 100; i++) {
System.out.println("main方法::" + i);
}
}
}
yield让步 正在执行的线程给给另一个线程让一下,然后继续执行,只是让了一步。
调用方法:
Thread.yield();
wait 等待 然正在执行的线程进入等待,需要被唤醒,没有唤醒将会一直等待下去,不会自己醒来。
使用方法:
t.wait();
finally 唤醒等待的线程
finallyAll唤醒所有等待的线程。
t.finally(); // 唤醒一个等待的线程
t.finallyAll(); // 唤醒所有等待的线程
下面给一个多线程使用的例子:
现有100张火车票,让5个人去卖票,直到卖完为止。
代码如下:
/**
* 多线程(卖火车票)
*
* @author FengYuan
*
*/
public class Test implements Runnable {
int ticket = 100; // 100张火车票
/*
* 实现Runnable接口然后重写run方法 因为火车票共享所以必须用Runnable
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
// 获取当前线程的名称
String name = Thread.currentThread().getName();
if (ticket > 0) {
System.out.println(name + "卖出的第" + ticket + "张票");
ticket--; // 每卖出一张减1
} else {
System.out.println("票买完了");
}
}
public static void main(String[] args) {
Test t = new Test();
for (int i = 0; i <= 22; i++) {
new Thread(t, "1号").start();
new Thread(t, "2号").start();
new Thread(t, "3号").start();
new Thread(t, "4号").start();
new Thread(t, "5号").start();
}
}
}
从上面的代码运行后我们可以看出卖的票有好多重复的,这就好比多个人同时买到的一张火车票,这样的话是不是存在很大的问题,所以有给出了一个方法,可以避免这种情况的发生。
synchronize线程锁,给线程加一把锁,在看效果。
代码如下:
/**
* 多线程(卖火车票)
*
* @author FengYuan
*
*/
public class Test implements Runnable {
int ticket = 100; // 100张火车票
/*
* 实现Runnable接口然后重写run方法 因为火车票共享所以必须用Runnable
*
* @see java.lang.Runnable#run()
*/
@Override
public synchronized void run() {
// 获取当前线程的名称
String name = Thread.currentThread().getName();
if (ticket > 0) {
System.out.println(name + "卖出的第" + ticket + "张票");
ticket--; // 每卖出一张减1
} else {
System.out.println("票买完了");
}
}
public static void main(String[] args) {
Test t = new Test();
for (int i = 0; i <= 22; i++) {
new Thread(t, "1号").start();
new Thread(t, "2号").start();
new Thread(t, "3号").start();
new Thread(t, "4号").start();
new Thread(t, "5号").start();
}
}
}
加锁之后的代码我们再次运行就会发现,票没有重复的了。
这是因为在线程没有上锁的情况下,就会有多个线程进同时进入方法,这样就会造成重复,而枷锁之后,线程每次只能执行一个,一个线程执行完后才能进入下一个线程。