多线程 进程:正在运行的应用程序(直译) 线程:就是进程的执行单元,一条执行路径 多线程:多线程就是一条应用程序有多条执行路径
一个进程中至少要有一个线程
开启多个线程是为了同时运行多部分的代码
每一个线程都有自己的运行内容,这个内容可以称之为线程要执行的任务
比如说:迅雷下载,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();
}
} |