•进程:一个启动的应用程序,是线程的载体.进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程
•线程是系统中最小的执行单元.JVM抢占式调度
•启动应用程序启动进程,进程默认情况下会启动主线程
进程与线程的区别
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
一.多线程的创建方式:
1. 继承(extends) Thread
- 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
- 创建Thread子类的实例,即创建了线程对象
- 调用线程对象的start()方法来启动该线程
//自定义中断标记,实现指定时间中断
class MyThread extends Thread {
private boolean needInterrupt = false;
public void setNeedInterrupt(boolean needInterrupt) {
this.needInterrupt = needInterrupt;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (needInterrupt) return;
System.out.println("打印" + i);
try {
sleep(1000L);
} catch (InterruptedException e) {
System.out.println("报错" + e.getMessage());
e.printStackTrace();
}
}
}}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.setName("ccc");
// thread.setPriority(10); 设置线程优先级
thread.start();
try {
thread.sleep(3000L);
thread.interrupt();
thread.setNeedInterrupt(true);
} catch (InterruptedException e) {
e.printStackTrace();
}}}
2.实现(implements) Runnable
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
- 调用线程对象的start()方法来启动线程。
Runnable不返回结果,也不能抛出被检查的异常
//多线程卖100张票
public class MyRunnable implements Runnable {
private int Ticket = 100;
@Override
public void run() {
while (true) {
synchronized (MyRunnable.class) { //用this也可
if (Ticket > 0) {
System.out.println("卖出第" + (101 - Ticket) + "张票");
Ticket--;
} else return;
}
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
Thread thread3 = new Thread(runnable);
thread1.setName("窗口1");
thread1.setName("窗口2");
thread1.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runnable接口的话,则很容易实现资源共享
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类
2.1 匿名内部类创建
使用线程的匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现Runnable接口,重写Runnable接口中的run方法:
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("美美" + i);
}
}
},"hello");
new Thread(thread).start();
System.out.println(thread.getName());
Thread.currentThread().setName("美眉"); //设置main线程名字
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+":妹"+i);
}
}
}
3.Callable
有返回值(可选),可以抛出异常
import java.util.concurrent.Callable;
public class MyCallAble implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
return "callable返回了数据";
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main2 {
public static void main(String[] args) {
MyCallAble callAble = new MyCallAble();
ExecutorService service = Executors.newFixedThreadPool(5);
Future<String> submit = service.submit(callAble);
try {
System.out.println(submit.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
二、线程安全
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全
1.同步代码块 synchronized
部分代码需要加锁
synchronized(同步锁){
需要同步操作的代码
}
同步锁:
- 锁对象 可以是任意类型。
- 多个线程对象 要使用同一把锁。(需要保证唯一)
2.同步方法
修饰符 synchronized 返回值类型 方法名(){
可能会产生线程安全问题的代码
}
同步锁:
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)
3.锁机制(同步锁/lock锁)
Lock lock = new ReentrantLock();
lock.lock();
采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,
使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生
Lock lock = ...;
lock.lock();
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
三.线程状态
NEW(新建)
线程刚被创建,但是并未启动。还没调用start方法。
Runnable (可运行)
线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操
作系统处理器。
Blocked(锁阻塞)
当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状
态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待)
一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个
状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
TimedWaiting(计时等待)
同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态
将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、
Object.wait。
Teminated(被终止)
因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡
四.线程通信(等待唤醒机制)
4.1. 2个线程间使用flag变量实现交替执行
//实现2个线程间交替执行
public class Demo {
int flag = 1;
public synchronized void method1() {
if (flag != 1) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("学");
System.out.print("习");
System.out.print("了");
System.out.print("吗");
System.out.println();
this.flag = 2;
this.notify();
}
public synchronized void method2() {
if (flag != 2) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("s");
System.out.print("t");
System.out.print("u");
System.out.print("d");
System.out.print("y");
System.out.println();
this.flag = 1;
this.notify();
}
}
public class Main {
public static void main(String[] args) {
Demo demo = new Demo();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
demo.method1();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
demo.method2();
}
}
}).start();
}
}
4.2 替换if为 whie可实现3个线程间有效通信(交替执行)
4.3 生产者消费者案例
//生产者
public class Producter extends Thread {
BaoZi bz;
@Override
public void run() {
int num = 0;
while (true) {
synchronized (bz) {
if (bz.flag == true) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("生产者唤醒,开始生产");
if (num % 2 == 0) {
bz.pier = "面皮";
bz.xianer = "猪肉大葱";
} else {
bz.xianer = "黑芝麻";
bz.pier = "米皮";
}
System.out.println("包子皮是:" + bz.pier + ";包子馅是:" + bz.xianer);
num++;
bz.flag = true;
bz.notify();
}
}
}
}
//消费者
public class Eater extends Thread {
BaoZi bz;
@Override
public void run() {
while (true) {
synchronized (bz) {
if (bz.flag == false) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("吃货开始吃包子,皮是:" + bz.pier + ";馅是:" + bz.xianer);
bz.flag = false;
bz.notify();
System.out.println();
}
}
}
}
public class BaoZi {
boolean flag = false;
String pier;
String xianer;
}
public class Main1 {
public static void main(String[] args) {
BaoZi baoZi = new BaoZi();
Producter pt = new Producter();
pt.bz = baoZi;
pt.start();
Eater eater = new Eater();
eater.bz = baoZi;
eater.start();
}
}
五.线程池 ExecutorService(interface)
5.1 合理使用的好处:
- 降低资源消耗。
- 提高响应速度。
- 提高线程的可管理性
官方建议使用Executors工程类来创建线程池对象
5.2 使用步骤
-
创建线程池对象。
-
创建Runnable接口子类对象。(task)
-
提交Runnable接口子类对象。(take task)
execute 无返回值
submit 有返回值
-
关闭线程池(一般不做) shutdown
public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(i+" "+Thread.currentThread().getName()); try { Thread.sleep(10L); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Main { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1); //Runtime.getRuntime().availableProcessors()获取当前cpu核心数 MyRunnable runnable = new MyRunnable(); service.execute(runnable); //execute 无返回值 Future<?> submit = service.submit(runnable); //submit 有返回值 } }
六.线程 join
•将线程之间的并行执行变为串行执行
七.守护线程 setDaemon
•守护线程是指在程序运行的时候在后台提供一种通用的服务的线程(垃圾回收线程)
•主线程执行结束,守护线程就结束
守护线程设置在线程启动之前.参数为true表示是daemon thread.
thread2.setDaemon(true);
thread2.start();








网友评论