牛客刷题后,我哭了。(多线程篇(一))
前言:写了一阵子博文了,是时候该在平台介绍一下自己了。哈哈哈哈哈,哈喽,艾瑞巴蒂,我就是李大代表一个双非二本在读大三生。(小声逼逼,现在因为疫情害怕寂寞孤单空虚冷的我跑到深圳准备找实习)。那么这个博文为什么要写呢?原因向下看IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
写了一段时间博文,学了一年的JAVA,整了几个不大不小的项目,着实感觉到了知识的加固和积累。唉呀妈呀我觉得我漂了,于是我屁颠屁颠的去刷了牛客各种面试题哈。在遭受了各种无情的巴掌之后我二话不说屁颠屁颠回来整理基础知识。(大代表的眼泪哗啦啦的往下流,太菜了)。
因此我决定,未来一段时间,在准备面试的过程中以及无限刷题和被打脸中,不断发起一系列我哭了之类的博文。希望正在面试和准备面试的同学朋友们一起努力哈。加油,奥利给。写的不好,不要喷的太狠,深圳打击已经让我深夜偷偷落泪了。
前奏:
进程是个啥子?
有进程才有线程,有线程才有多线程。那么什么是进程?来张图说明问题。。
进程其实就是一个程序执行的一次过程,是系统运行程序的基本单位。运行吗,所以必然是动态的。再者你运行一个小程序就是进程创建-》运行-》死亡的一个简单过程。
线程是个啥子?
线程又是啥?线吗,肯定更小(哈哈),线程比进程更小是一个更小的执行单位。当我们有一个进程的时候,此时会有很多很多的线程被产生(打开游戏–同一界面好多角色,有不同的动作),同类线程共享代码和数据空间(内存空间,系统资源),而且每个线程都有独立的运行栈和程序计数器(PC),因此同类线程彼此切换负担较小,可以说是轻量级的进程
多线程又是个啥子?
门前有条土路,20年前它只能过一个车。20年后变成一条高速路,同一时刻好多好多的车可以同时经过。你打王者的时候,一个界面,好多英雄并且同时有很多动作。现实有很多同时进行的例子,可以说是多任务同一时间。书面化语言(多线程就是多个线程同时运行或交替运行。单核CPU的话是顺序执行,也就是交替运行。多核CPU的话,因为每个CPU有自己的运算器,所以在多个CPU中可以同时运行。)
多线程好处?
开辟多线程==开辟多条路径。几年后你写的网站被几亿人同时访问(高并发),如果你只有一条路死定了。如果利用好多线程机制,那么可以大大提高系统整体的并发能力以及性能。
多线程创建的三种方式
①继承Thread类
package com.lidadaibiao.csdn;
public class ThreadTest {
public static void main(String[] args) {
Test1 t1 = new Test1();
t1.start();
}
}
class Test1 extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<10;i++){
System.out.println("lidadaibiao"+i);
}
}
}
``
这里看到出来,继承Thread之后,Test1变成一个系统子任务,CPU不确定去调用,随机的去调用run。
②实现Runnable接口
package com.lidadaibiao.csdn;
public class ThreadTest {
public static void main(String[] args) {
Test1 t1 = new Test1();
new Thread(t1).start();
System.out.println("lidadaibiao---?");
}
}
class Test1 implements Runnable{
@Override
public void run() {
System.out.println("lidadaibiao");
}
}
③实现Callable
package com.lidadaibiao.csdn;
import java.util.concurrent.Callable;
public class ThreadTest {
public static void main(String[] args) throws Exception {
Test1 t1 = new Test1();
System.out.println("lidadaibiao---?");
t1.call();
}
}
class Test1 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Integer a=1;
for(;a<10;a++){
System.out.println(""+a);
}
return a;
}
}
前两种的简单对比。
子类继承Thread具备了多线程能力
启动线程: 子类对象.start()
不建议使用:避免OOP(面向对象)单继承局限
实现接口Runnable具有多线程能力
启动线程: 传入目标对象+Thread对象.start()
推荐使用: OOP多实现,灵活方便,方便同一 份对象的代理。
线程的五大状态瞧一下!
(注:图片来源网络,如有侵权,请联系本人,立马删除)
线程相关方法
currentThread() : 返回对当前正在执行的线程对象的引用。
getName() : 返回该线程的名称。
setPriority(int newPriority) 设置优先级
getPriority() 线程的优先级代表的是概率 范围从1到10,默认为5
getState() : 返回该线程的状态。
stop()停止线程 (不推荐使用)
interrupt() : 中断线程。(推荐使用)
interrupted() : 测试当前线程是否已经中断。
isAlive() : 测试线程是否处于活动状态
setDaemon() 可以将指定的线程设置成后台线程,守护线程; 创建用户线程的线程结束时,后台线程也随之消亡; 只能在线程启动之前把它设为后台线程
isDaemon() : 测试该线程是否为守护线程。
sleep () 使线程停止运行一段时间,将处于阻塞状态
如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行!
join () 阻塞指定线程等到另一个线程完成以后再继续执行。
yield () 让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态;
调用了yield方法之后,如果没有其他等待执行的线程,此时当前线程就会马上恢复执行! •
sleep
sleep(时间)指定当前线程阻塞的毫秒数,时间达到后线程进入就绪状态,可以模拟网络延时、倒计时。每一个对象都有一个锁,sleep不会释放锁。
package com.lidadaibiao.csdn;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.sound.midi.Soundbank;
public class ThreadTest {
public static void main(String[] args) throws Exception {
System.out.println(new GregorianCalendar().getTime());
new Thread(new Test1()).start();
}
}
class Test1 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(5000);
System.out.println("lidadaibiao---5s");
System.out.println(new GregorianCalendar().getTime());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
join
阻塞指定线程等到另一个线程完成以后再继续执行。插队。
package com.lidadaibiao;
import java.util.GregorianCalendar;
public class JoinDemon {
public static void main(String[] args) {
Thread t = new Father();
t.start();
}
}
class Father extends Thread{
@Override
public void run() {
System.out.println("想喝水,发现没了");
Thread t = new Thread(new Son());
t.start();//让儿子去买
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("儿子迷路,出去找儿子");
}//买的过程爸爸不能喝水 阻塞
System.out.println("儿子回来了把零钱给了儿子");
}
}
class Son extends Thread{
@Override
public void run() {
System.out.println("接过钱,去买水");
System.out.println("看到好玩的地方了");
for(int i = 0 ;i<10;i++){
//路上玩了10秒
try {
Thread.sleep(1000);
System.out.println("疯狂玩的第:"+new GregorianCalendar().getTime()+"秒");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("儿子玩丢了");
}
}
System.out.println("想起买水。赶紧去");
System.out.println("儿子到家了");
}
}
很明显,儿子买水的过程中,父亲无法喝水,只有等到儿子回来 父亲才能把零钱给儿子。儿子线程插队了。
priorit:(优先级)
用数字表示1-10;
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
(这里的优先级只是概率问题,并不是一定优先级大就最先。可以自己测试一下。)
Daemon(守护线程)
多线程分为守护线程和用户线程。
用户线程:运行在前台,执行具体的任务,如程序的主线程就是用户线程。(人类迟早会老去)
守护线程:运行在后台,为其他前台线程服务.也可以说守护线程是JVM中非守护线程的 “守护者”。(上帝一直存在,并守护我们)
package com.lidadaibiao;
public class DeamonTest {
public static void main(String[] args) {
God god = new God();
Thread t1 = new Thread(god);
t1.setDaemon(true);//将上帝调整为守护线程
t1.start();
new Thread(new People()).start();
}
}
class People implements Runnable{
@Override
public void run() {
for(int i = 0;i<10;i++){
System.out.println("i am people");
}
}
}
class God implements Runnable{
@Override
public void run() {
for(;true;){
System.out.println("i am god");
}
}
}
没有设置守护线程的截图。
并发与并行
并发:同一个对象多个线程同时操作。
并行:指同时发生两个并发事件,具有并发的含义。并发不一定并行,也可以说并发事件之间不一定要同一时刻发生。
好比你吃饭的时候进来一个电话,边接边吃(并发),放下筷子接电话接完电话在吃饭(并行)。
线程同步
因为同一个进程(会产生很多线程)共享同一快的存储空间,虽然方便但也有一些弊端。比如访问冲突问题,多个线程访问同一个资源甚至想去修改这一资源。我们就必须用到线程同步,线程同步(我理解的意思是线程不管被修改还是访问都必须同步给其他共享该资源的线程,也就是中间要有等待,需要一个同步时间),也就是一个等待机制,多个需要同时访问此对象的线程进入这个对象的等待 池形成队列前面使用结束后面才能使用,才能有相应的同步时间。
访问冲突问题解决!!
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带 来了访问冲突的问题。为了保证数据在方法中被访问时的正确性,在访问 时加入锁机制(synchronized),当一个线程获得对象的排它锁,独占资源, 其他线程必须等待,使用后释放锁,后面才能去访问。
synchronized
synchronized 块
package com.lidadaibiao;
import java.util.ArrayList;
import java.util.List;
/**
* 线程同步快
* @author lidadaibiao
*
*/
public class UnsafeDemon {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i = 0;i<10000;i++){
new Thread(()->{
//同步快
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
System.out.println(list.size() );
}
}
未使用
最后结果不可能是1000大家可以Test一下。
synchronized 同步方法
package com.lidadaibiao;
public class SynTest01 {
public static void main(String[] args) {
//一份资源
SafeWeb12306 web =new SafeWeb12306();
//多个人去分享,可能会重复或者负数
new Thread(web,"daibiao1").start();
new Thread(web,"daibiao2").start();
new Thread(web,"daibiao3").start();;
}
}
class SafeWeb12306 implements Runnable{
//票数
private int ticketNums =10;
private boolean flag = true;
@Override
public void run() {
while(flag) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
test();
}
}
//线程安全 同步
public synchronized void test() {
if(ticketNums<=0) {
flag = false;
return ;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
}
}
未使用关键字,出现了重复和0.这是因为每个线程(用户在买票的过程中)出现了抢夺,好比daibiao1开始取票的时候,daibiao2准备取票,daibiao1取完了系统还没有做同步(缺少时间等待)然后daibiao2直接取票这时就出现了。daibiao1和daibiao2都10的情况,daibiao3同理。至于0和负数的情况。就是daibiao1取票的时候刚好daibiao3在取票这时只有1张,3取完了,1取得时候刚好更新就出现了0的情况。(我是这么理解的,哈哈哈)
累了累了!!!今天就到这吧。明天继续总结,高级部分的一些内容吧。
头发越来越少。。哈哈哈哈
上一篇: Android字符串和十六进制相互转化出现的中文乱码问题
下一篇: Android事件传递机制
推荐阅读