案例1:在吃饭的时候,边吃饭边玩手机
案例2:也可以边上厕所边玩手机
多任务处理是指用户可以在同一时间内运行多个应用程序,每个应用程序被称作一个任务.Linux、windows就是支持多任务的操作系统,比起单任务系统它的功能增强了许多。
当多任务操作系统使用某种任务调度策略允许两个或更多进程并发共享一个处理器时,事实上处理器在某一时刻只会给一件任务提供服务。因为任务调度机制保证不同任务之间的切换速度十分迅速,因此给人多个任务同时运行的错觉。多任务系统中有3个功能单位:任务、进程和线程。
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
例如在一个抢票网站中,100张票,有1000个人来抢,然而这1000个人其中每个人就可以理解为一个线程。
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如[Windows 7](https://baike.baidu.com/item/Windows 7/0?fromModule=lemma_inlink)的线程,进行混合调度。
同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。
一个进程可以有很多线程,每条线程并行执行不同的任务。
在多核或多CPU,或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见,即提高了程序的执行吞吐率。在单CPU单核的计算机上,使用多线程技术,也可以把进程中负责I/O处理、人机交互而常被阻塞的部分与密集计算的部分分开来执行,编写专门的workhorse线程执行密集计算,从而提高了程序的执行效率。
多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理” [1]。
当没有线程的情况
for (int i = 0; i < 50; i++) {
System.out.println("newThread " + i);
}
for (int i = 0; i < 50; i++) {
System.out.println("main " + i);
这边是当上面的代码全部执行完毕,才回执行后面的代码
创建方式-运行主类继承 Thread 类, 并重写 run 方法,最后调用 start 方法开启线程
public class Test2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("newThread " + i);
}
}
public static void main(String[] args) {
Test2 test2 = new Test2();
test2.start();
for (int i = 0; i < 50; i++) {
System.out.println("main " + i);
}
}
}
可以看到其中有代码穿插的情况
注意:由于继承的局限性,需要考虑好使用情况
实现方式:编写类,实现 Runnable 接口,重写run方法,调用时需要new Thread对象,并将实现 Runnable 接口的实例化对象传入,并调用Thread的start方法,来运行线程
下面是代码演示:
public class Test2 extends Thread{
public static void main(String[] args) {
new Thread(new ThreadTest()).start();
for (int i = 0; i < 50; i++) {
System.out.println("main " + i);
}
}
}
class ThreadTest implements Runnable{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("newThread " + i);
}
}
}
简化写法-采用Lambda表达式
public static void main(String[] args) {
new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println("newThread " + i);
}
}).start();
for (int i = 0; i < 100; i++) {
System.out.println("main " + i);
}
}
实现方式1:
public class Test2{
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> stringFutureTask = new FutureTask<>(new TestCallable());
new Thread(stringFutureTask).start();
String s = stringFutureTask.get();
System.out.println(s);
}
}
class TestCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
return "你好!";
}
}
实现方式2:
public class Test2{
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程池对象
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 创建线程执行对象
TestCallable t1 = new TestCallable();
TestCallable t2 = new TestCallable();
// 提交线程对象到线程池-并获取到执行结果
Future<String> submit1 = executorService.submit(t1);
Future<String> submit2 = executorService.submit(t2);
// 获取返回值数据
String res1 = submit1.get();
String res2 = submit2.get();
// 输出返回值
System.out.println(res1);
System.out.println(res2);
// 关闭线程池对象
executorService.shutdownNow();
}
}
class TestCallable implements Callable<String>{
@Override
public String call() throws Exception {
String name = Thread.currentThread().getName();
for (int i = 0; i < 100; i++) {
System.out.println(name + " " + i);
}
return name;
}
}
public class Test2{
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程池对象
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 创建线程执行对象
TestCallable t1 = new TestCallable();
// 提交线程对象到线程池
for (int i = 0; i < 10; i++) {
executorService.submit(t1);
}
// 关闭线程池对象
executorService.shutdownNow();
}
}
class TestCallable implements Callable<String>{
@Override
public String call() throws Exception {
String name = Thread.currentThread().getName();
for (int i = 0; i < 1000; i++) {
System.out.println(name + " " + i);
}
return name;
}
}
总结:
interface Eat{
void eat();
}
class My implements Eat{
@Override
public void eat() {
System.out.println("我吃饭了!!");
}
}
class EatAgent implements Eat{
private My user;
public EatAgent(My user) {
this.user = user;
}
@Override
public void eat() {
before();
user.eat();
after();
}
private void before(){
System.out.println("准备食材做饭·");
}
private void after(){
System.out.println("你吃完了,收钱,收拾残局·");
}
}
My my = new My();
my.eat();
My my = new My();
EatAgent eatAgent = new EatAgent(my);
eatAgent.eat();
new EatAgent(new My()).eat();
底层实现就是采用代理的形式进行的。
线程一共有五大状态如下:
建议:
测试案例:
public class Test2 implements Runnable{
public static void main(String[] args){
Test2 thread = new Test2();
new Thread(thread, "线程1").start();
for (int i = 0; i < 1000; i++) {
if(i == 800){
System.out.println("主线程到800次,让线程停止!");
thread.stop();
}
System.out.println("主线程执行 --> " + i);
}
}
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println(Thread.currentThread().getName() + " -- > " + i++);
}
}
public void stop(){
flag = false;
}
}
模拟抢票同时操作一个线程
public class Test2 implements Runnable{
private int ticketNum = 10;
public static void main(String[] args){
Test2 thread = new Test2();
new Thread(thread, "小红").start();
new Thread(thread, "小黄").start();
new Thread(thread, "小花").start();
}
@Override
public void run() {
while (true){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticketNum > 0){
System.out.println(Thread.currentThread().getName() + "抢了第 " + ticketNum-- + "张票~");
}else{
break;
}
}
}
}
当线程在运行的时候有了阻塞的时候,就可能会出现上面的情况。
所以模拟延时可以方法问题的发生性。
礼让情景测试(Thread.yield() 可以开启礼让)
没有礼让情况
public class Test2 implements Runnable{
public static void main(String[] args){
Test2 thread = new Test2();
new Thread(thread, "a").start();
new Thread(thread, "b").start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "--开始");
System.out.println(Thread.currentThread().getName() + "--结束");
}
}
public class Test2 implements Runnable{
public static void main(String[] args){
Test2 thread = new Test2();
new Thread(thread, "a").start();
new Thread(thread, "b").start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "--开始");
Thread.yield();
System.out.println(Thread.currentThread().getName() + "--结束");
}
}
礼让成功
礼让失败
可以将这个理解为插队,当当前的线程执行过join方法后就会强制先执行完当前线程,后续才回执行其他线程。
案例:
public class Test2 implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Test2());
thread.start();
for (int i = 0; i < 200; i++) {
Thread.sleep(1);
if(i == 30){
thread.join();
}
System.out.println("main --> " + i);
}
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " --> " + i);
}
}
}
当main执行到30的时候,我们的线程就开始调用join了。开始插队。
当线程跑完了后,主线程才接着执行。
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread.State state = thread.getState();
System.out.println(state);
thread.start();
state = thread.getState();
System.out.println(state);
while (true){
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
if(Thread.State.TERMINATED == state){
break;
}
}
}
注意:在线程到结束转台就不可以在去调用start了,否则就会报错
当有的线程比较重要需要先执行的时候,就可以给线程设置优先级(被设置优先级很高的线程更容易先执行)
线程的等级为1~10默认的优先级为5
public class Test2 implements Runnable{
public static void main(String[] args) {
Test2 test2 = new Test2();
Thread t1 = new Thread(test2);
Thread t2 = new Thread(test2);
Thread t3 = new Thread(test2);
Thread t4 = new Thread(test2);
Thread t5 = new Thread(test2);
Thread t6 = new Thread(test2);
// 主线程默认等级为 5
System.out.println(Thread.currentThread().getName() + " --> " + Thread.currentThread().getPriority());
t2.setPriority(1);
t3.setPriority(4);
t4.setPriority(Thread.MAX_PRIORITY); // 10
t5.setPriority(8);
t6.setPriority(3);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " --> " + Thread.currentThread().getPriority());
}
}
当设置后,优先级较高的会被优先执行(但是不保真-相当于你 9/10 概率 他1/10 概率,但是还是有可能他中奖)
守护线程的作用就是和用户线程区分开的一种特殊的线程,即使守护线程没有执行完毕,程序依然会结束。
启动守护线程需要调用setDaemon 方法传入 true就开始守护线程了--默认值为false不开始守护线程
测试:
public static void main(String[] args) {
Thread t1 = new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("用户线程执行 --> " + i);
}
});
Thread t2 = new Thread(()->{
for (int i = 0;; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("守护线程执行 --> " + i);
}
});
// 将线程设置为守护线程
t2.setDaemon(true);
// 启动线程
t1.start();
t2.start();
}
这里可以看到我们设置的守护进程为永真,但是在用户线程执行完毕后,守护线程没有执行完,但是程序依然结束了
,这就是守护线程的作用。
有多个线程同时来操作同一个数据,就称为并发。
例如前面讲的案例,多人同时去购买一张票,就会导致出现票数为负数的情况。
线程不安全案例:
public class Test2 implements Runnable{
private int money = 200;
public static void main(String[] args) {
Test2 test2 = new Test2();
new Thread(test2, "a").start();
new Thread(test2, "b").start();
new Thread(test2, "c").start();
}
@Override
public void run() {
if (money > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
money -= 100;
System.out.println(Thread.currentThread().getName() + "-->" + 100);
System.out.println("余额:" + this.money);
}
}
}
案例二:
public static void main(String[] args) throws InterruptedException {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->list.add(1)).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
ArrayList 就是一个线程不安全的案例,当多线程去往里面添加数据的时候,可能会操作到同一个数据导致添加失败~
队列就是让所有线程来操作对象的时候,就不能同时的去操作对象数据,
需要进行排序,前面的操作完了,后面的才可以在去进行操作。
优点:可以让数据变为安全,不会出现数据异常的情况
缺点:效率会大大降低
锁就是为了实现队列这个效果二产生的,目的就是锁住可能会被多人同时操作的对象,让他们有序排序,不能一起来操作
public class Test2 implements Runnable{
private int money = 200;
public static void main(String[] args) {
Test2 test2 = new Test2();
new Thread(test2, "a").start();
new Thread(test2, "b").start();
new Thread(test2, "c").start();
}
@Override
public synchronized void run() {
if (money > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
money -= 100;
System.out.println(Thread.currentThread().getName() + "-->" + 100);
System.out.println("余额:" + this.money);
}else{
System.out.println("余额不足了!");
}
}
}
不论运行多少次,都不会在出现为负数的这种情况了。
案例(解决集合添加问题):
public static void main(String[] args) throws InterruptedException {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(1);
}
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
当对象被锁后,不论怎么操作都不会影响数据的完整性。
代码示例:
public class Test2 implements Runnable{
public static String compute = "电脑";
public static String phone = "手机";
public boolean flag;
public Test2(boolean flag) {
this.flag = flag;
}
public static void main(String[] args){
Test2 t1 = new Test2(false);
Test2 t2 = new Test2(true);
new Thread(t1, "我").start();
new Thread(t2, "你").start();
}
@Override
public void run() {
if(flag){
synchronized (compute){
System.out.println(Thread.currentThread().getName() + "拿到了" + compute + "的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (phone){
System.out.println(Thread.currentThread().getName() + "拿到了" + phone + "的锁");
}
}
}else{
synchronized (phone){
System.out.println(Thread.currentThread().getName() + "拿到了" + phone + "的锁");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (compute){
System.out.println(Thread.currentThread().getName() + "拿到了" + compute + "的锁");
}
}
}
}
}
运行之后代码会一直卡死:
原因是当前的我拿到了手机的锁--然后你拿到了电脑的锁,我现在又想接着去拿电脑的锁,但是发现你拿了,所以我就需要等待,然后你也需要拿手机的锁,你也在等我,所以就导致了你等我-我等你--最终代码程序锁死。
下面是解决方案:
我们可以让一个线程一次性别拿多吧锁,先吧手中的锁释放了在去拿新的,就可以解决这个问题。
public class Test2 implements Runnable{
public static String compute = "电脑";
public static String phone = "手机";
public boolean flag;
public Test2(boolean flag) {
this.flag = flag;
}
public static void main(String[] args){
Test2 t1 = new Test2(false);
Test2 t2 = new Test2(true);
new Thread(t1, "我").start();
new Thread(t2, "你").start();
}
@Override
public void run() {
if(flag){
synchronized (compute){
System.out.println(Thread.currentThread().getName() + "拿到了" + compute + "的锁");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (phone){
System.out.println(Thread.currentThread().getName() + "拿到了" + phone + "的锁");
}
}else{
synchronized (phone){
System.out.println(Thread.currentThread().getName() + "拿到了" + phone + "的锁");
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (compute){
System.out.println(Thread.currentThread().getName() + "拿到了" + compute + "的锁");
}
}
}
}
修改代码,完成测试!
死锁的避免方法:
线程不安全测试代码:
public class Test2{
public static void main(String[] args) {
LockTest lockTest = new LockTest();
new Thread(lockTest).start();
new Thread(lockTest).start();
new Thread(lockTest).start();
}
}
class LockTest implements Runnable{
private int ticketNum = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
if (ticketNum > 0){
System.out.println(Thread.currentThread().getName() + " --> " +ticketNum--);
}else{
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
可能会操作到同一个对象
采用Locl锁后:
public class Test2{
public static void main(String[] args) {
LockTest lockTest = new LockTest();
new Thread(lockTest).start();
new Thread(lockTest).start();
new Thread(lockTest).start();
}
}
class LockTest implements Runnable{
private int ticketNum = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
if (ticketNum > 0){
System.out.println(Thread.currentThread().getName() + " --> " +ticketNum--);
}else{
break;
}
lock.unlock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
推荐写法
public class Test2{
public static void main(String[] args) {
LockTest lockTest = new LockTest();
new Thread(lockTest).start();
new Thread(lockTest).start();
new Thread(lockTest).start();
}
}
class LockTest implements Runnable{
private int ticketNum = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();
if (ticketNum > 0){
System.out.println(Thread.currentThread().getName() + " --> " +ticketNum--);
}else{
break;
}
}finally {
lock.unlock();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
对比
代码实现生产者消费者
public class Test2{
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
Producer producer = new Producer(synContainer);
Consumer consumer = new Consumer(synContainer);
new Thread(producer).start();
new Thread(consumer).start();
}
}
// 生产者
class Producer implements Runnable{
public SynContainer synContainer;
public Producer(SynContainer synContainer) {
this.synContainer = synContainer;
}
// 生产
@Override
public void run() {
for (int i = 1; i < 101; i++) {
synContainer.producer(new Fish(i));
System.out.println("生产了第 " + i + " 个产品!");
}
}
}
class Consumer implements Runnable{
public SynContainer synContainer;
public Consumer(SynContainer synContainer) {
this.synContainer = synContainer;
}
// 消费
@Override
public void run() {
for (int i = 1; i < 101; i++) {
System.out.println("消费 --> " + synContainer.consumer().fishId + " 个产品!");
}
}
}
// 产品
class Fish{
public int fishId;
public Fish(int fishId) {
this.fishId = fishId;
}
}
// 缓冲区
class SynContainer{
private Fish[] fishs = new Fish[10];
private int count = 0;
public SynContainer() {
}
// 生产
public synchronized void producer(Fish fish){
// 如果产品满了
if(count == fishs.length){
// 停止生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产产品
fishs[count++] = fish;
// 提醒用户可来取
this.notify();
}
// 消费
public synchronized Fish consumer(){
// 如果当前没有产品了
if(count == 0){
// 停止消费--需要等待生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 开始消费
Fish fish = fishs[--count];
// 通知生成者生产
this.notify();
return fish;
}
}
线程池的使用:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test2 implements Runnable{
public static void main(String[] args) {
// 创建线程池对象--并指定创建10条线程
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 添加线程需要执行的任务
executorService.execute(new Test2());
executorService.execute(new Test2());
executorService.execute(new Test2());
executorService.execute(new Test2());
// 关闭线程池对象
executorService.shutdownNow();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
在上面的添加执行任务的时候可以使用execute(没有返回值),也可以使用submit(有返回值)
评论
登录后才可以进行评论哦!