Java基础之多线程(持续更新中)

论坛 期权论坛 脚本     
匿名网站用户   2020-12-19 19:43   11   0

多线程
进程:正在运行的应用程序(直译)
线程:就是进程的执行单元,一条执行路径
多线程:多线程就是一条应用程序有多条执行路径

一个进程中至少要有一个线程

开启多个线程是为了同时运行多部分的代码

每一个线程都有自己的运行内容,这个内容可以称之为线程要执行的任务

比如说:迅雷下载,360杀毒,生活中也有多线程,刷牙洗脸烧水例子

多线程并不是同时执行的

比如,现在开启的程序有两个,这两个程序并没有同时执行,cpu只是在做着特别特别特别快的切换,在同一时刻,只能运行一个线程。切换的频率是非常快的,随机的(由时间片决定,也就是cpu分配给每个程序的时间)。如果程序开的特别多,那么被切换到的概率和频率就小了,所以卡机。想要不卡机,弄两个cpu,到后来才叫双核,四核的。

没有学多线程前,我们做的是多线程程序吗?JVM,GC演示

按理来说,我们并没有看到多个线程在同步执行,但是通过分析,至少有两个线程

1.因为main函数执行的时候,需要一个线程,该线程的任务代码就是main函数里内容

2.一个匿名对象,这个匿名对象new完了就垃圾了,垃圾回收器回收又是一个线程

3.不是说主线程执行完毕后虚拟机就执行完了,虚拟机要等待其他线程执行完毕

public class Demo

{

public static void main(String[] args) throws Exception

{

new Demo2();

new Demo2();

new Demo2();

//如果不手动调用回收垃圾函数,GC机制会在垃圾多的时候在回收

System. gc();

System.out.println( "哈哈");

}

}

class Demo2 extends Object

{

public void finalize()

{

System.out.println( "我被回收啦!" );

}

}

for(int x = -999999; x < 999999; x++){}控制代码的延迟


为什么使用线程?什么时候使用?
为了提高效率才使用,只有要操作代码的内容比较复杂,比较耗时,多的时候使用

如何实现多线程程序?
线程依赖于进程的存在。进程是由操作系统创建的。java不能直接调用操作系统的功能进程是无法通过系统来获取的。不像是c,c++,但是java可以调用底层c写好的类,线程类Thread。因为main函数就是一个线程,我们想是Java.lang包下的!
通过查看API,我们知道创建线程的方式有两种
方式1 :

继承Thread类
并且重写Thread中的run()方法

为什么要这样?

创建线程的目的是为了开启一条运行路径,去运行指定的代码和其他线程代码同时运行

JVM创建的主线程的任务都定义在了主函数中

Thread类用于描述线程,线程是需要任务的,所以Thread类也对任务进行了描述,这个描述就是通过Thread类中的run函数体现的。也就是说,run函数就是封装了运行任务的函数。

直接创建Thread的子类对象创建线程

调用start函数开启线程并调用线程的任务run()函数执行

public class Demo

{

public static void main(String[] args) throws Exception

{

Demo2 d1 = new Demo2( "旺财");

Demo2 d2 = new Demo2( "小强");

d1.start();

d2.start();

}

}

class Demo2 extends Thread

{

private String threadName;

public Demo2(String threadName)

{

this. threadName = threadName;

}

public void run()

{

for( int x = 1; x <= 10; x++)

{

System. out.println( threadName + x);

}

}

}

线程的一些函数

Thread getName()获取线程的名称 Thread-编号(从0开始)

Thread.currentThread().getName()获取的是当前线程的名字

Demo d = new Demo("旺财");

public Demo(String name)

{

super(name)

}

调用父类的一个参数构造函数给线程名字

线程运行问题

主线程和其他线程互不干扰,各自调用各自的函数,出现异常只结束当前线程,不会结束JVM

线程的5种状态

方式2:

实现Runnable接口

覆写接口中的run函数,将线程的任务代码都封装到run函数中

通过Thread类创建线程对象,并将Runnable接口的子类作为Thread类的构造函数的参数进行传递

调用线程对象的start函数,开启线程

class MyRunnable implements Runnable

{

public void run()

{

for ( int x = 0; x < 100; x++)

{

System.out.println(Thread.currentThread().getName() + "---hello"+ x);

}

}

}

public class MyRunnableDemo

{

public static void main(String[] args)

{

MyRunnable my = new MyRunnable();

Thread t1 = new Thread(my);

Thread t2 = new Thread(my);

t1.setName( "乔峰");

t2.setName( "慕容复");

t1.start();

t2.start();

}

}

为啥有第一种了还要用第二种方案
避免了第一种单继承的局限性
将线程中任务从线程子类中分离出来,进行了单独的封装。避免了只为了用父类的某个功能而继承

卖票案例和IllegalStateException

class MyThreadImpl implements Runnable

{

private int ticket = 100;//不用static,不局限性,因为每个线程都是作为Runnable接口的子类进行传递

@Override

public void run()

{

while( true)

{

if( ticket > 0)

{

//因为父接口里面没有抛出异常,所以子类只能try处理

try{Thread. sleep(10);}catch (InterruptedException e) {}

//这里出现了线程安全问题,可能同时进来了,没有一个一个if判断

//那么就会出现-1 -2等情况

System. out.println(Thread. currentThread().getName() + "正在出售第" + ticket-- + "张票!");

}

}

}

}

public class Demo

{

public static void main(String[] args)

{

MyThreadImpl mti = new MyThreadImpl();

Thread t1 = new Thread(mti);

Thread t2 = new Thread(mti);

Thread t3 = new Thread(mti);

t1.setName( "线程一");

t2.setName( "线程二");

t3.setName( "线程三");

t1.start();

t1.start();

Exception in thread "main"异常所在线程java.lang.IllegalThreadStateException异常类型

at java.lang.Thread.start(Thread.java:595)

at Demo.main(Demo.java:23)异常发生的位置

t2.start();

t3.start();

}

}

卖票发生出现负数的票原因

1.在多线程的情况下

2.有共享数据被多个线程所操作

3.操作共享数据的代码有多条

同步代码块解决问题

class MyThreadImpl implements Runnable

{

Object obj = new Object();

private int ticket = 100;//不用static,不局限性,因为每个线程都是作为Runnable接口的子类进行传递

@Override

public void run()

{

while( true)

{

synchronized( obj)//obj标识,任意对象

{

if( ticket > 0)

{

System. out.println(Thread. currentThread().getName() + "正在出售第" + ticket-- + "张票!");

}

}

}

}

}

同步代码块,同步函数

synchronized(obj )

{

}

同步的好处:解决了线程安全的问题

同步的弊端:相对的降低了效率,在同步代码块的代码每次都会判断

同步的前提:在多线程中,并且使用同一把锁

publicsynchronizedvoid show(){}

同步函数的锁是this,同步代码块的锁是任意对象

public staticsynchronizedvoid show()

静态的同步函数使用的锁是 该函数所属的字节码文件对象

可以用getClass方法获取,也可以使用类名.class表示

单例懒汉式在多线程中

class Single

{

private Single(){}

private static Single s = null;

//第一种同步函数方式

public synchronized static Single getInstance()

{

if( s == null)

s = new Single();

return s;

}

//第二种同步代码块

public static Single getInstance2()

{

//解决效率问题

if( s == null)

{

//静态代码块的锁是字节码文件

synchronized(Single. class)

{

if( s == null)

s = new Single();

}

}

return s;

}

}

面试死锁例子

class MyLock

{

public static final Object lockA = new Object();

public static final Object lockB = new Object();

}

class DieLock implements Runnable

{

private boolean flag;

public DieLock( boolean flag)

{

this. flag = flag;

}

public void run()

{

if( flag)

{

synchronized(MyLock. lockA)

{

System.out.println( "true-lockA");

synchronized(MyLock. lockB)

{

System. out.println( "true-lockB");

}

}

}

else

{

synchronized(MyLock. lockB)

{

System. out.println( "false-lockB");

synchronized(MyLock. lockA)

{

System. out.println( "false-lockA");

}

}

}

}

}

public class Demo

{

public static void main(String[] args)

{

DieLock d1 = new DieLock( true);

DieLock d2 = new DieLock( false);

Thread t1 = new Thread(d1);

Thread t2 = new Thread(d2);

t1.start();

t2.start();

}

}

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP