/*
多线程:
进程:正在进行中的程序
也就是在内存中的开辟的一块空间
线程:负责程序执行的一条执行路径
也可以称为执行单元
一个进程至少要有一个线程
也就是进程的执行实际上是线程在执行
当一个程序中有多个线程的时候,这个程序就是多线程程序
多线程一定能提高效率吗?不一定能提高效率,但是能合理的使用cpu资源
最关键的是能够同时执行同一个程序中的多个功能(多部分代码)
JVM是不是多线程的呢?
至少会有一个线程负责程序的正常执行,执行main方法中的代码----主线程
还得有一个线程负责垃圾回收,执行finalize()方法中的代码---垃圾回收线程
多线程的好处可以实现多个功能的同时执行,或相同的功能同时执行
任务:进程中的每个线程执行的代码
每个任务代码都有其存储位置
比如:主线程的任务代码存储在main方法中
垃圾回收线程的代码存储在finalize方法中
执行的任务不同,任务代码的存储位置就不同
线程是随着任务的存在而存在
随着任务的执行结束而从内存中消失
*/
class Test
{
//重写fialize()方法,定义对象被回收的方式,该方法被垃圾回收线程执行
public void finalize()
{
System.out.println("被回收了");
}
}
class Demo1
{
public static void main(String[] args) //主线程---一个进程至少会有一个主线程
{
new Test();
new Test();
new Test();
//每次运行的结果都不一样,这就是多线程的随机性,因为多个线程在抢夺cpu
System.gc();//运行垃圾回收线程,就是把cpu让给垃圾回收线程
System.out.println("Hello World!");
}
}
/*
任金鹏和彭立要同时显示姓名和次数
既然要同时显示,就要用多线程
如何自己创建线程?
第一种方式:
1:定义一个类继承Thread
2:重写run方法
创建线程是为了执行任务,任务代码需要有存储位置
run()方法就是这个存储位置
3:创建子类的实例
4:调用start()启动线程
注意:主线程执行的代码在main方法中
自定义线程执行的代码在run方法中
run()和start的区别:
run()方法是一个普通的方法,不具备启动线程的功能,只是一个普通的方法调用
start方法可以启动线程并去执行run()中的任务代码
为什么继承Thread类?
public static void main(String[] args)
{
Thread t1 = new Thread();
Thread t2 = new Thread();
t1.start();
t2.start();
以上这段代码没有执行结果,我们创建线程是为了执行任务,
任务代码需要有存储位置,run方法就是存储位置
因为Thread类中的run方法没有实现任何功能,而我们创建的线程的
任务代码还必须写在run方法中,所以只能是继承Thread类,并
重写run方法
}
*/
class Test extends Thread
{
private String name;
public Test(String name)
{
this.name = name;
}
public void run()//定义了任务
{
for(int i=1;i<=10;i++)
{
//子线程的名字是 Thread-编号 编号从0开始
System.out.println(Thread.currentThread().getName()+"...show..."+i);
}
}
}
class Demo2
{
public static void main(String[] args)
{
Test t1 = new Test("任金鹏");//创建了一个线程
Test t2 = new Test("彭立");//创建了一个线程
t2.start();//启动线程
t1.start();//启动线程
for(int i=1;i<=10;i++)
{
//主线程的名字是 main
System.out.println(Thread.currentThread().getName()+"...show..."+i);
}
}
}
//线程栈中的异常:进程中的某个线程发生异常,只是该线程自己的事儿,不会影响其它线程
//进程中的所有线程都执行完,进程才会结束
class Test extends Thread
{
private String name;
public Test(String name)
{
this.name = name;
}
public void run()
{
int[] arr = new int[2];
for(int i=1;i<=10;i++)
{
System.out.println(arr[2]);
System.out.println(Thread.currentThread().getName()+"...show..."+i);
}
}
}
class Demo3
{
public static void main(String[] args)
{
Test t1 = new Test("任金鹏");
Test t2 = new Test("彭立");
t2.start();
t1.start();
// System.out.println(5/0);
}
}
/*
四个窗口同时卖票
既然是同时卖票,就要用多线程
创建四个线程,每个线程都是卖票
因为都是卖票,所以四个线程执行的任务代码是相同的
只要定义一个类继承Thread类,并重写run方法就o了
为了让四个线程共享100张票,需要把票修饰成static的
实际使用中票不应该修饰为静态的,因为票会有很多种
那怎么解决共享100张票的问题?
创建线程的第二种方式:
1:定义一个类实现Runnable接口
2:重写接口中的run方法
3:创建该子类的实例
4:创建Thread类的实例,实际上就是在创建线程
5:把子类的实例作为参数传递给Thread类的构造方法
创建线程的目的是 执行任务,而任务代码定义在实现Runnable接口的子类中
所以为了让线程执行任务代码,只能是把实现了Runnable接口的子类对象作为参数传递给线程对象
6:启动线程
第二种方式的特点:
1:线程任务和线程对象实现了分离,更加的面向对象
创建好了线程对象,执行什么任务不再重要,只要是实现了Runnable接口的子类对象,就可以作为参数传递给
线程对象,
2:实现接口的同时还可以再继承其它类
*/
class Ticket implements Runnable//这是一个用来描述任务的类
{
private int num = 50;
public void run()
{
while(true)
{
if(num>0)
{
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
class Demo4
{
public static void main(String[] args)
{
//Eat e = new Eat();
Ticket t = new Ticket();
Thread t0 = new Thread(e);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t0.start();t1.start();t2.start();t3.start();
/*
Ticket t0 = new Ticket();
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
t0.start();
t1.start();
t2.start();
t3.start();
*/
}
}
/*
为什么创建线程的第二种方式就能解决卖票问题?
第一种方式创建线程类的同时还得定义任务,任务和线程是绑定在一起的,每创建一个线程都会创建一份儿资源(任务用到的数据)
所以,创建四线程就创建了四份资源
第二种方式:单独对任务进行描述,只需要创建一个任务对象(只有一份资源),分别让四个线程去执行
为什么Thread类也实现Runnable接口?run方法是提取出的共性的方法
第二种方式的关键是,线程任务和线程对象的分离
建议使用第二中方式
*/
class Test implements Runnable//extends Thread
{
public void run()
{
}
}
class Demo6
{
public static void main(String[] args)
{
Test t = new Test();
Thread d = new Thread(t);
}
}
/*
线程出现了线程安全问题:
打印了0,-1,-2等错票
线程出现安全问题的原因:
1:多个线程操作了共享数据
2:操作共享数据的语句有多条,当一个线程得到cpu,执行了操作共享数据的多条代码的一部分时,还没执行完,就被其它
线程抢走了cpu
解决方式:当一个线程在执行操作共享数据的多条代码时,其它线程不能参与执行,只有该线程把所有操作共享数据的代码全部执行完
其它线程才能参与执行
解决线程安全问题的方式:同步代码块
synchronized(对象)//对象是任意的
{
需要被同步的代码块
}
同步代码块的弊端:不能进入同步代码块的线程要反复的判断锁,降低性能
使用同步需满足的条件:
1:至少有两个或两个以上的线程
2:多个线程使用同一把锁
*/
class Ticket implements Runnable
{
private int num = 50;
private Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(num>0)
{
//线程在睡的时候不会释放锁,只是放弃了cpu
try{Thread.sleep(10);}catch(InterruptedException e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
}
class Demo6
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t0.start();t1.start();t2.start();t3.start();
}
}
/*
有一个银行,可以存钱
有两个储户同时存钱,每个人存三次,每次存100
*/
class Bank
{
private int sum =0;
private Object obj = new Object();
public synchronized void add(int money)//同步函数
{
sum = sum +money;
//---->1 ----->2
System.out.println("sum="+sum);
}
}
//描述任务
class Customer implements Runnable
{
private Bank bank = new Bank();
public void run()
{
for(int i=1;i<=3;i++)
{
bank.add(100);
}
}
}
class Demo7
{
public static void main(String[] args)
{
//创建任务对象
Customer customer = new Customer();
Thread t1 = new Thread(customer);
Thread t2 = new Thread(customer);
t1.start();
t2.start();
}
}
//同步函数的锁?锁是用来实现多个线程之间的互斥,所以同步函数也必须有锁
//同步函数使用的锁是this
/*
静态同步函数:静态进内存的时候没有使用new创建的对象,但是有其所属的类 的字节码文件对象
这个对象是 Class类型的对象
所以它使用的锁是 其所属的类 的字节码文件对象 类名.class
*/
class Ticket implements Runnable
{
private static int num = 50;
private Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(Ticket.class)
{
if(num>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
else
{
while(true)
func();
}
}
public static synchronized void func()//锁是Ticket.class
{
if(num>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
class Demo8
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
t0.start();
//让主线程停一会儿,这时候活着的线程只有t0,所以t0一判断flag的值为true,就进入了同步代码块
try{Thread.sleep(10);}catch(InterruptedException e){e.printStackTrace();}
//主线程睡醒后把flag的值改为false,当t1线程得到cpu的时候flag的值就是fasle了,从而执行同步函数
t.flag = false;
t1.start();
}
}
//单例设计模式中懒汉式并发访问的问题
class Single
{
private static final Single s = new Sigle();
private Single(){}
public static Single getInstance()
{
return s;
}
}
class Test implements Runnable
{
public void run()
{
Single2 s = Single2.getInstance();
}
}
class Single2
{
private static final Single2 s = null;
private Single2(){}
public static Single2 getInstance()
{
//从第三个线程开始就不用判断锁了
if(s==null)
{
synchronized(Single2.class)
{
if(s==null)
//--->1 --->2
s = new Single2();
}
}
return s;
}
}
class
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
//死锁:
class Ticket implements Runnable
{
private static int num = 2000;
private Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(obj)
{
func();
}
}
}
else
{
while(true)
func();
}
}
public synchronized void func()
{
synchronized(obj)
{
if(num>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
class Demo10
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
t0.start();
//让主线程停一会儿,这时候活着的线程只有t0,所以t0一判断flag的值为true,就进入了同步代码块
try{Thread.sleep(20);}catch(InterruptedException e){e.printStackTrace();}
//主线程睡醒后把flag的值改为false,当t1线程得到cpu的时候flag的值就是fasle了,从而执行同步函数
t.flag = false;
t1.start();
}
}
//线程间的通信: 多个线程执行的任务不同,但是操作的资源相同
//描述资源
class Resource
{
String name;
String sex;
}
//定义输入线程执行的任务
class Input implements Runnable
{
private Resource res;
private Object obj = new Object();
public Input(Resource res)
{
this.res = res;
}
public void run()
{
int i=0;
while(true)
{
synchronized(res)
{
if(i==0)
{
res.name = "彭立";
res.sex = "男男";
}
else
{
res.name = "徐才舒";
res.sex = "女女女女女女女女女女女";
}
}
i = (i+1)%2;//i不是共享数据
}
}
}
//定义输出线程执行的任务
class Output implements Runnable
{
private Resource res;
public Output(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
synchronized(res)
{
System.out.println(res.name+"==="+res.sex);
}
}
}
}
class Demo11
{
public static void main(String[] args)
{
//创建资源对象
Resource res = new Resource();
//创建输入线程的任务对象
Input input = new Input(res);
//创建输出线程的任务对象
Output output = new Output(res);
//创建输入线程
Thread in = new Thread(input);
//创建输入线程
Thread out = new Thread(output);
in.start();
out.start();
}
}
/*
一下三个方法必须用在同步中:因为同步中才有锁
wait():等待的线程会释放锁,等待的线程被放入了线程池
notify():唤醒线程池中的任意一个线程
notifyAll():唤醒线程池中的所有线程
为什么wait(),notify(),notifyAll()定义在Object中?
这三个方法都要用锁,而只有同步中才有锁,而同步中的锁是任意的对象
任意对象都能调用的方法只能定义在Object中
*/
class Resource
{
String name;
String sex;
boolean flag = false;
}
//定义输入线程执行的任务
class Input implements Runnable
{
private Resource res;
private Object obj = new Object();
public Input(Resource res)
{
this.res = res;
}
public void run()
{
int i=0;
while(true)
{
synchronized(res)
{
if(res.flag)
try{res.wait();}catch(InterruptedException e){e.printStackTrace();}//让持有res这个锁的线程去等待
if(i==0)
{
res.name = "彭立";
res.sex = "男男";
}
else
{
res.name = "徐才舒";
res.sex = "女女女女女女女女女女女";
}
res.flag = true;
res.notify();//空唤醒
}
i = (i+1)%2;//i不是共享数据
}
}
}
//定义输出线程执行的任务
class Output implements Runnable
{
private Resource res;
public Output(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
synchronized(res)
{
if(!res.flag)
try{res.wait();}catch(InterruptedException e){e.printStackTrace();}
System.out.println(res.name+"==="+res.sex);
res.flag = false;
res.notify();
}
}
}
}
class Demo12
{
public static void main(String[] args)
{
//创建资源对象
Resource res = new Resource();
//创建输入线程的任务对象
Input input = new Input(res);
//创建输出线程的任务对象
Output output = new Output(res);
//创建输入线程
Thread in = new Thread(input);
//创建输入线程
Thread out = new Thread(output);
in.start();
out.start();
}
}