1、进程
1.进程是正在运行的程序,也就是执行程序的一次执行过程,是系统进行资源分配的基本单位
2.目前操作系统都是支持多进程,可以进行执行多个进程,通过进程ID区分
3.单核cpu在同一时刻,只能有一个进程,宏观并行,微观串行。
2、线程
线程,又称轻量级进程,进程中的一条执行路径,也是cpu的基本调度单位。一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。
例如:迅雷是一个进程,当中的多个下载任务即为线程。
Java虚拟机是一个继承,当中默认包含主线程(main),可通过代码创建多个独立线程,与main并发执行。
3、进程与线程的区别
1.进程是操作系统资源分配的基本单位,而线程是cpu的基本调度单位。
2.一个程序运行后至少有一个进程。
3.一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义。
4.进程间不能共享数据段地址,但是同进程的线程之间可以。
4、创建线程的三种方法
4.1 继承Thread类 重写run方法
例子:使用线程Thread类实现4个窗口各卖100张票
//继承线程
public class Test01 extends Thread{
int ticket = 20;
//重写run方法
@Override
public void run() {
while (true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
ticket--;
}
}
}
}
public class Test02 {
public static void main(String[] args) {
//创建线程
Test01 test01 = new Test01();
Test01 test02 = new Test01();
Test01 test03 = new Test01();
Test01 test04 = new Test01();
test01.setName("窗口1:");
test02.setName("窗口2:");
test03.setName("窗口3:");
test04.setName("窗口4:");
//启动线程
test01.start();
test02.start();
test03.start();
test04.start();
}
}
4.2 实现Runnable接口
例子:四个窗口共卖100张票
class Test01{
public static void main(String[] args) {
RunnableTicket r1 = new RunnableTicket();
Thread t1 = new Thread(r1,"窗口A:");
Thread t2 = new Thread(r1,"窗口B:");
Thread t3 = new Thread(r1,"窗口C:");
Thread t4 = new Thread(r1,"窗口D:");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
public class RunnableTicket implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticket > 0) {
ticket--;
System.out.println(Thread.currentThread().getName() + "卖了一张,剩余:" + ticket + "张");
/*ticket = ticket - 1;*/
} else {
return;
}
}
}
}
}
4.3 实现Callable接口
例子:求和
public class Test03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
My task = new My();
My2 task2 = new My2();
//自建创建线程对象并提交Callable类型的任务是比较麻烦的,需要封装到一个FutureTask类种, 建议使用线程池来提交任务
/*FutureTask futureTask = new FutureTask(task);
Thread t1 = new Thread(futureTask);
t1.start();
System.out.println(futureTask.get());*/
ExecutorService executorService = Executors.newFixedThreadPool(5);
Future<Integer> submit = executorService.submit(task);
Future<Integer> submit2 = executorService.submit(task2);
Integer sum = submit.get();
Integer sum2 = submit2.get();
System.out.println(sum+sum2);
}
}
class My implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 50; i++) {
sum+=i;
}
return sum;
}
}
class My2 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 51; i <= 100; i++) {
sum+=i;
}
return sum;
}
}
5、线程常用的方法
5.1 休眠sleep
public static void sleep(long millis) 当前线程主动休眠millis毫秒。
/*线程休眠*/
public class TestSleep {
public static void main(String[] args) {
T t = new T();
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("main:================"+i);
}
}
}
class T extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
/*线程休眠*/
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("*************"+i);
}
}
}
5.2 yield
public static void yield()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
yield()只让有相同执行权的线程获得cup时间片,
但是yield()不能控制cup交出的时间,
yeild()只是让线程恢复到就绪状态,
那么可能在执行yeild()后进入就绪状态,然后马上又进入运行状态。
public class TestYield {
public static void main(String[] args) {
T2 t1 = new T2();
T2 t2 = new T2();
t1.start();
t2.start();
}
}
class T2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
/*两个线程交替频率提高*/
Thread.yield();
System.out.println(Thread.currentThread().getName()+"********"+i);
}
}
}
5.3 加入join
public final void join() 允许其他线程加入到当前线程中 在main函数线程中调用线程tj.join()方法,此时main函数线程就进入阻塞状态, 直到线程tj完全执行完以后,线程main才结束阻塞状态。
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
T3 t1 = new T3();
t1.start();
t1.join();//把t1线程加入到当前mian线程中,只有t1线程结束后,main线程才会执行
/*主线程*/
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"主线程:************"+i);
}
}
}
class T3 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"------------"+i);
}
}
}
5.4 守护线程Daemon
线程对象.setDaemon(true);设置为守护线程。 线程有两类:用户线程(前台线程)和守护线程(后台线程) 如果程序中所有前台线程都执行完毕了,后台线程也会自动结束。 垃圾回收线程属于守护线程。
public class TestDaemon {
public static void main(String[] args) {
T4 t1 = new T4();
t1.setDaemon(true);//设置t1线程为守护线程,当前台线程(main线程)结束时,守护线程也会自动结束
t1.start();
/*主线程*/
for (int i = 0; i <20; i++) {
System.out.println(Thread.currentThread().getName()+"main:***********"+i);
}
}
}
class T4 extends Thread{
@Override
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName()+"--------"+i);
}
}
}
6、线程的安全问题
A线程要把“Hello”存入数组中;B线程要把“Word”存入数组中。
/*线程安全问题*/
public class Test01 {
private static String[] arr = new String[5];
private static int index = 0;
public static void main(String[] args) throws InterruptedException {
//匿名对象
Runnable hello = new Runnable() {
@Override
public void run() {
synchronized (arr) {
if (arr[index] == null) {
arr[index] = "hello";
index++;
}
}
}
};
//匿名对象
Runnable word = new Runnable() {
@Override
public void run() {
synchronized (arr) {
if (arr[index] == null) {
arr[index] = "word";
index++;
}
}
}
};
Thread t1 = new Thread(hello);
Thread t2 = new Thread(word);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(Arrays.asList(arr));
}
}
7、线程死锁
当A线程拥有锁资源a时,这时A线程需要锁资源b, 而B线程拥有锁资源b,这时B线程需要锁资源a, 这样会导致A等待B线程释放资源b, B线程等待A线程释放锁资源a。 从而二个处于永久等待。从而操作死锁。
例子:两个人打游戏,小明有键盘,小刚有鼠标 。要打游戏,小明要等小刚的鼠标,而小刚要等小明的键盘,这俩个人都在循环等待,就会陷入死锁状态。
public class LockObject {
public static Object a = new Object();
public static Object b = new Object();
}
public class BoyOne extends Thread{
@Override
public void run() {
synchronized (LockObject.a){
System.out.println(Thread.currentThread().getName()+"获取键盘");
synchronized (LockObject.b){
System.out.println(Thread.currentThread().getName()+"获取鼠标");
System.out.println("可以打游戏了");
}
}
}
}
public class BoyTwo extends Thread{
@Override
public void run() {
/*try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
synchronized (LockObject.b){
System.out.println(Thread.currentThread().getName()+"获取鼠标 ");
synchronized (LockObject.a){
System.out.println(Thread.currentThread().getName()+"获取键盘");
System.out.println("可以打游戏了");
}
}
}
}
//测试
public class DeadLock {
public static void main(String[] args) {
BoyOne b1 = new BoyOne();
BoyTwo b2 = new BoyTwo();
b1.setName("小明");
b2.setName("小刚");
b1.start();
b2.start();
}
}
8、什么是线程池
线程池,--其实就是一个 容纳多个线程的容器 ,其中的线程可以反复使用,省去了频繁创建线程对象的操作 ,--无需反复创建线程而消耗过多资源。
使用线程池的好处:
9、创建线程池的方式
所有的线程池---封装了一个父接口---java.util.concurrent.Executor.
它的实现接口: ExecutorService.
有一个工具类。Executors可以帮你创建相应的线程池。
//创建单一线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
//创建定长线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
//创建可变线程池
ExecutorService executorService = Executors.newCachedThreadPool();
//创建延迟线程池
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
public static void main(String[] args) {
/**
*int corePoolSize:核心线程数
*int maximumPoolSize:最大线程数
*long keepAliveTime:空闲时间
*TimeUnit unit:时间单位
*BlockingQueue<Runnable> workQueue:堵塞队列
*/
//LinkedBlockingDeque:可以设置堵塞等待的个数,如果不设置个数,则默认值为Integer的最大值
BlockingDeque blockingDeque =new LinkedBlockingDeque(3);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,5,5, TimeUnit.SECONDS,blockingDeque);
for (int i = 0; i < 5; i++) {
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":---------");
}
});
}
//等待线程执行结束时,关闭线程
threadPoolExecutor.shutdown();
}
使用Callable接口,创建线程
package demo06;
import java.util.concurrent.*;
public class Test03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
My task = new My();
My2 task2 = new My2();
//自建创建线程对象并提交Callable类型的任务是比较麻烦的,需要封装到一个FutureTask类种, 建议使用线程池来提交任务
/*FutureTask futureTask = new FutureTask(task);
Thread t1 = new Thread(futureTask);
t1.start();
System.out.println(futureTask.get());*/
ExecutorService executorService = Executors.newFixedThreadPool(5);
Future<Integer> submit = executorService.submit(task);
Future<Integer> submit2 = executorService.submit(task2);
Integer sum = submit.get();
Integer sum2 = submit2.get();
System.out.println(sum+sum2);
}
}
class My implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 50; i++) {
sum+=i;
}
return sum;
}
}
class My2 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 51; i <= 100; i++) {
sum+=i;
}
return sum;
}
}
10、手动锁-Lock
Lock它是手动锁的父接口,它下面有很多实现类。
lock()方法。
unlock()释放锁资源,放在finally
package demo07; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Test01 { public static void main(String[] args) { Ticket task = new Ticket(); Thread t1 = new Thread(task,"窗口A"); Thread t2 = new Thread(task,"窗口B"); Thread t3 = new Thread(task,"窗口C"); Thread t4 = new Thread(task,"窗口D"); t1.start(); t2.start(); t3.start(); t4.start(); } } class Ticket implements Runnable{ private int ticket = 100; Lock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock();//加锁 if (ticket > 0) { --ticket; System.out.println(Thread.currentThread().getName() + "卖了一张,剩余:" + ticket + "张"); } else { break; } }finally { lock.unlock();//释放锁 } } } }