一、进程和线程
- 进程 :电脑中每一个单独运行的程序都是一个独立的进程,进程之间是相互独立的
- 线程:线程依赖于进程的支持,是进程中的最小执行单位
- 多线程:一个时间段上可以同时运行多个程序,程序将进行资源的轮流抢占
Java是多线程的编程语言,Java在进行并发访问的处理的时候,可以得到更高的处理性能
线程是一个动态执行的过程,它是一个从产生到死亡的过程,其生命周期如下
线程的优先级
每一个Java线程都有一个优先级,取值范围为1到10,默认为5
二、 创建线程
1、继承Thread类本身
继承Thread类,重写run方法,调用start方法开启线程
public class Test {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();//mt.start()调用系统资源,启动新的线程,新的线程会主动执行run()方法,谁start的就
for (int j= 0; j< args.length; j++) {
System.out.println("main j = "+j);
}
}
}
//定义一个类继承Thread
class MyThread extends Thread{
@Override
//重写run方法
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run i = "+i);
}
}
}
2、实现Runnable接口
实现Runnable接口,调用run方法,调用start执行
public static void main(String[] args) {
// 3.创建Runnable实现类对象
MyRunnable mr = new MyRunnable();
// 4.创建Thread并传入实现类对象
Thread t = new Thread(mr);
// 5.启动线程
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("main: " + i);
}
}
}
// 1.定义Runnable接口的实现类型
class MyRunnable implements Runnable {
// 2.重写run方法
@Override
public void run() { // run方法在子线程上面执行
for (int i = 0; i < 20; i++) {
System.out.println("run: " + i);
}
}
}
3、通过Callable和 Future 创建线程
1、创建Callable的接口实现类并实现call方法
2、创建Callable实现类的实例,使用FutureTask类来包装Callable对象
3、使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程
4、调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值
public class CallableThreadTest implements Callable<Integer> {
public static void main(String[] args)
{
CallableThreadTest ctt = new CallableThreadTest();
FutureTask<Integer> ft = new FutureTask<>(ctt);
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
if(i==20)
{
new Thread(ft,"有返回值的线程").start();
}
}
try
{
System.out.println("子线程的返回值:"+ft.get());
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception
{
int i = 0;
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
}
三、多线程
原理:CPU同一时间只能处理一个线程,其实是CPU在多个线程之间快速切换造成的"假象
- 创建线程需要消耗CPU和内存线程太多CPU切换太频繁,每个线程执行到的机会就少了
- 好处:不会提高程序的执行速度,让电脑同时干活,可以提CPU的使用效率,提高资源的利用率
线程安全
多个线程访问某个方法的时候,不管这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。
如何确保线程安全
synchronized 同步代码块
synchronize(锁对象){
//要同步的代码块
}
操作共享资源的代码需要被同步;
保证同步代码块里面的代码同时只有一个线程操作,其他的线程在外面等
必须使用同一个对象作为锁
public class demo2 {
public static void main(String[] args) {
SaleTicket t1 = new SaleTicket();
t1.setName("窗口1");
SaleTicket t2 = new SaleTicket();
t2.setName("窗口2");
SaleTicket t3 = new SaleTicket();
t3.setName("窗口3");
t1.start(); // t1.start() -> 新的线程执行t1.run();
t2.start(); // t2.start() -> 新的线程执行t2.run();
t3.start(); // t3.start() -> 新的线程执行t3.run();
}
}
// 卖票的线程
class SaleTicket extends Thread {
private static int ticketNum = 100; // ticketNum = 1
private static Object obj = new Object();
@Override
public void run() {
while (true) {
// 任意对象都可以作为锁,但是必须是同一把锁
synchronized (obj) { // t2 // t1
// 操作共享数据的代码都需要锁住
if (ticketNum > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
ticketNum--;
System.out.println(getName() + " 卖了一张票,剩余 " + ticketNum);
} else {
break;
}
}
Thread.yield(); // t3 //
// 当CPU执行到这行代码,告诉CPU,我这个线程不太想执行了,CPU切换到其他线程去,尽量切换
// 礼让
// try {
// Thread.sleep(2);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
}
lock 锁
提供了比synchronize方法和语句可获得的更广泛的锁定操作。
class SaleTicket extends Thread {
private static int ticketNum = 100; // ticketNum = 1
// 锁要是同一把
private static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
// 任意对象都可以作为锁,但是必须是同一把锁
// synchronized (obj) { // 在同步代码块开始的地方上锁
lock.lock(); // 上锁 // t3
try {
// 操作共享数据的代码都需要锁住
if (ticketNum > 0) { // t2
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
ticketNum--;
System.out.println(getName() + " 卖了一张票,剩余 " + ticketNum);
} else {
break;
}
} finally {
lock.unlock();
}
// } // 在同步代码块结束的地方释放锁
Thread.yield(); // t1 // 当CPU执行到这行代码,告诉CPU,我这个线程不太想执行了,CPU切换到其他线程去,尽量切换
}
}
}
线程池
Runnable接口
- 定义一个类实现Runnable接口
- 重写run方法
- 创建Runnable实现类对象
- 创建一个线程池
- 提交任务
- 关闭线程池
线程池的执行任务过程
- 刚创建好的线程池,没有任务,线程池中的线程会等待
- 当添加一个任务的时候,线程池会派其中一个线程去执行这个任务
- 如果线程池中的任务比线程多的时候,后面的任务会等待,当前面的线程执行完任务,就会来执行后面的任务
- 如果任务全部执行完成,线程池中的线程也接着等待
- 当线程池销毁的时候,里面的线程也销毁
public static void main(String[] args) {
// 3.创建Runnable实现类对象
MyRunnable mr = new MyRunnable();
// 4.创建一个有固定线程数量的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
// ThreadPoolExecutor
// System.out.println(pool);
// 5.往提交任务
pool.submit(mr); // 线程池就派其中的一个线程来执行mr中的run方法
pool.submit(mr); // 线程池就派其中的一个线程来执行mr中的run方法
pool.submit(mr); // 线程池就派其中的一个线程来执行mr中的run方法
pool.submit(mr); // 线程池就派其中的一个线程来执行mr中的run方法
// 6.关闭线程池
pool.shutdown();
}
}
// 1.定义一个类实现Runnable接口
class MyRunnable implements Runnable {
// 2.重写run方法
@Override
public void run() { // 在子线程上面执行的任务
String name = Thread.currentThread().getName();
for (int i = 0; i < 1; i++) {
System.out.println(name + " : " + i);
}
}
Callable接口
作用是放在子线程上执行代码
- 定义一个类实现Callable接口
- 重写call方法
- 创建Callable接口实现类对象
- 创建线程池
- 往线程池里面提交方法
- 关闭线程池
- *可以同时往线程池中提交Runnable和Callable任务
public static void main(String[] args) {
// 3.创建Callable接口实现类对象
MyCallable mc = new MyCallable();
MyRunnable mr = new MyRunnable();
// 4.创建线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
// 5.往线程池里面提交任务
pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
pool.submit(mr);
// 6.关闭线程池
pool.shutdown();
}
}
// 1.定义一个类实现Callable接口
class MyCallable implements Callable {
// 2.重写call方法
@Override
public Object call() throws Exception {
String name = Thread.currentThread().getName();
for (int i = 0; i < 1; i++) {
System.out.println(name + " : " + i);
}
return null;
}
}
//1.定义一个类实现Runnable接口
class MyRunnable implements Runnable {
// 2.重写run方法
@Override
public void run() { // 在子线程上面执行的任务
String name = Thread.currentThread().getName();
for (int i = 0; i < 1; i++) {
System.out.println(name + " 222222: " + i);
}
}