------- android培训、java培训、期待与您交流! ----------
11.01 概述
线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread
对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。
当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main
方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:
- 调用了
Runtime
类的exit
方法,并且安全管理器允许退出操作发生。 - 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到
run
方法之外的异常。
创建新执行线程有两种方法。一种方法是将类声明为 Thread
的子类。该子类应重写 Thread
类的 run
方法。接下来可以分配并启动该子类的实例。例如,计算大于某一规定值的质数的线程可以写成:
class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
然后,下列代码会创建并启动一个线程:
PrimeThread p = new PrimeThread(143); p.start();
创建线程的另一种方法是声明实现 Runnable
接口的类。该类然后实现 run
方法。然后可以分配该类的实例,在创建 Thread
时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示:
class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
然后,下列代码会创建并启动一个线程:
PrimeRun p = new PrimeRun(143); new Thread(p).start();
每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。
/*
进程:是一个正在执行中的程序
每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
线程:就是进程中一个独立的控制单元
线程在控制着进程的执行
一个进程中至少有一个线程
Java Vm 启动的时候就有一个进程java.exe
该进程中至少有一个线程在负责java程序的执行
而且这个线程运行的代码存在于main方法中
该线程称之为主线程
扩展:其实jvm,启动的时候不只一个线程,还有负责垃圾回收机制的线程
1。如何在自定义的代码中,自定义一个线程呢?
通过对api(Application Programming Interface,
应用程序编程接口)的查找,java已经提供了对线程这类事物的描述,就是Thread类
11.02 创建线程-继承Thread类
创建线程的第一种方式:继承Thread类
步骤;
1,定义类,继承Thread类
2。复写run方法
目的:将自定义的代码存储在run方法中,让线程运行
3,创建一个对象,执行start方法,
该方法有两个作用:启动线程,调用run
发现运行结果每一次都不同
因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行
明确一点,在某一个时刻,只能有一个程序执行(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果
我们可以形象把多线程的运行形容为在互相抢夺cpu的执行权
这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长,cpu说的算
这个过程是随机的
11.03 run start的特点
为什么要覆盖run方法呢?
Thread用于描述线程
该类就定义了一个功能(run function),用于存储线程要运行的代码,该功能就是run方法
也就是Thread类中的run方法,用于存储线程要运行的代码(主线程存在于main方法中)
*/
class Demo extends Thread
{
public void run()
{
for (int x=0;x<60;x++)
System.out.println("demo run--"+x);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo();//创建好了一个线程
d.start();//开启线程,并调用该线程的run方法
//d.run();//仅仅是调用了对象方法,线程没有启动,是主线程的动作
for (int x = 0;x < 60;x++ )
{
System.out.println("Hello World!--"+x);
}
}
}
11.04 线程练习
第11天-06-多线程(获取线程对象以及名称)
/*
线程都有自己的名称,通过getName函数获取
Thread-编号,该编号从0开始
static Thread currentThread();获取当前线程对象
设置线程名称:setName或者构造函数(super关键字)
*/
class Test extends Thread
{
// private String name;
Test(String name)
{
//this.name = name;//构造函数和成员变量不能在成员方法中,是独立存在的
super(name);
}
public void run()
{
for (int x= 0;x < 60; x++)
{
System.out.println((Thread.currentThread()==this)+"--test--"+x+"--"+this.getName());
}
}
}
class ThreadTest
{
public static void main(String[] args)
{
Test t1 = new Test("one--");
Test t2 = new Test("two++");
t1.start();
t2.start();
for (int x = 0;x < 60; x++)
{
System.out.println("--main--" + x);
}
}
}
11.05 线程运行状态
![](https://i-blog.csdnimg.cn/blog_migrate/21badb14ca8802cc1d0652bf6e4588e9.png)
11.07 售票的例子
11.08实现RUNABLE接口
/*
需求:简单的卖票程序
多个窗口同时卖票
程序运行后发现了一个问题:
1.卖票的时候有重复行为,这时考虑将tick设置为静态共享数据
但是静态有其局限性;
2.也可以考虑创建一个线程让他执行四次,但是运行会报告线程异常,一个线程被启动了四次,明显是
一种浪费而且不符合程序设计初衷
3。这时就考虑线程创建的问题了
------------------------------
线程创建的第二种方式:实现Runnable接口
步骤:
1.定义类实现Runnable接口
2,覆盖Runnable接口中的run方法
将线程要运行的代码存在于该run方法中
3,通过Thread类建立线程对象
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
5.调用Thread类的strat方法开启线程并调用Runnable接口子类的run方法
------------------------------------
实现方式和继承方式有什么区别吗?
继承方式时,只能单继承,想要实现多线程就要创建一个子类对象,子类对象运行的时候会调用run方法,多个对象调用的时候会重复调用run方法中的代码,等于多个人在做一件事情,是一种浪费
实现方式时,将Runnable接口的子类创建的对象作为参数传递给Thread的构造函数,这时Thread类创建的对象会运行多个线程,但是执行的代码是同一块代码,等于多个人在一起做一件事情,效率更高也没有浪费
-------------------------------------------------------------
实现方式好处:避免了单继承的局限性
在定义线程时,建议使用实现方式
*/
class Ticket implements Runnable
{
private //static int tick = 100;
public void run()
{
while (true)
{
if (tick > 0)
System.out.println("sale : "+ tick--);
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();/该对象非线程
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();//这四个对象此时调用start方法调用的run方法,调用的Ticket类的对象只有一个,就是Runnable类接口的子类所创建的Ticket类对象t
}
}
11.09 多线程的安全的问题
11.10同步代码块
/*
通过分析,发现打印出0.-1.-2等错票
多线程的运行出现了安全问题:
问题的原因:
当多条线程在操作同一个共享数据时,一条线程对这个数据只执行了一部分,,还没有执行完
另一个线程参与进来执行,导致共享数据的错误(代码中tick的判断出现了错误)
解决方案:
对多条线程执行操作共享数据时,只能让一个线程都执行完,在执行过程中其他不可以参与执行
java中内置了解决方式:
同步代码块
synchronized(对象)
{
需要被同步的代码
}
同步的前提;
1。必须要有两个或两个以上的线程
2。必须是多个线程使用同一个锁
必须保证同步中只有一个线程在运行
好处;解决了多线程的安全问题
弊端:多个线程都需要判断锁,较为消耗资源
*/
class Ticket implements Runnable
{
private int tick = 10000;
Object obj = new Object();
public void run()
{
while (true)
{
synchronized (obj)
{
if (tick > 0)
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}//这个异常不能抛,因为是接口类型
System.out.println(Thread.currentThread().getName()+"sale : "+ tick--);
}
}
}
}
class TicketDemo2
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
11.11 同步函数
/*
需求:
银行有一个金库,有两个储户分别存300元,每次存100,存3次
目的:该程序是否有安全问题,如果有,如何解决?
如何找问题;
1,明确哪些代码是多线程运行代码
2,明确共享数据
3。明确多线程运行代码中哪些语句是操作共享数据的
*/
class Bank
{
private int sum;
Object obj = new Object();
public synchronize//或则 void add(int n)
{
synchronized(obj)
{
sum = sum + n;
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}//这个函数可以抛出
System.out.println("sum="+sum);
}
}
}
class Cus implements Runnable
{
private Bank b = new Bank();
public void run()
{
for (int x = 0; x < 3; x++)
{
b.add(100);
}
}
}
class BankDemo
{
public static void main(String[] args)
{
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
11.12 同步函数的锁是this
/*
同步函数使用的锁是this
*/
class Ticket implements Runnable
{
private int tick = 100;
boolean flag = true;
public void run()
{
if (flag)
{
while (true)
{
synchronized(this)
{
if(tick > 0)
{
try{Thread.sleep(10);}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"--code--"+tick--);
}
}
}
}
else
while (true)
show();
}
public synchronized void show()
{
if (tick > 0)
{
try{Thread.sleep(10);}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"--show--"+tick--);
}
}
}
class ThislockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
try{Thread.sleep(10);}catch (Exception e){}
t.flag = false;
t2.start();
}
}
11.13 静态同步函数的锁是Class对象
/*
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证发现不再是this了
静态方法中也不会有this
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象
类名.class,该对象的类型是Class
静态的同步方法使用的锁是该方法所在类的字节码文件对象,类名.class
*/
class Ticket implements Runnable
{
private static int tick = 100;
//Object obj = new Object();
boolean flag = true;
public void run()
{
if (flag)
{
while (true)
{
synchronized(Ticket.class)
{
if(tick > 0)
{
try{Thread.sleep(10);}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"--code--"+tick--);
}
}
}
}
else
while (true)
show();
}
public static synchronized void show()
{
if (tick > 0)
{
try{Thread.sleep(10);}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"--show--"+tick--);
}
}
}
class StaticMethodDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
try{Thread.sleep(10);}catch (Exception e){}
t.flag = false;
t2.start();
}
}
11.14 单例设计模式 懒汉式
class Single//懒汉式
{
private static Single s = null;
private Single(){}
public static //synchronized Single getInstance()//可以用同步函数或者同步代码块解决多线程访问的安全问题,但是相比较同步代码块效率更高
{
if (s==null)//多线程访问时会存在安全问题,需要同步
{
synchronized(Single.class)// 什么意思?
{ if(s==null)
s = new Single();
}
}
retrun s;
}
}
//饿汉式
class Single
{
private static final Single s = new Single();
private Single(){}
public static getInstance()
{
return s;
}
}
11.15多线程-死锁
/*
死锁;
同步中嵌套同步
*/class Ticket implements Runnable
{
private int tick = 100;
boolean flag = true;
Object obj = new Object();
public void run()
{
if (flag)
{
while (true)
{
synchronized(obj)
{
show();
}
}
}
else
while (true)
show();
}
public synchronized void show()
{
synchronized(obj)
{
if(tick > 0)
{
try{Thread.sleep(10);}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"--code--"+tick--);
}
}
}
}
class DeadLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
try{Thread.sleep(10);}catch (Exception e){}
t.flag = false;
t2.start();
}
}
简单版本:
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag=flag;
}
public void run();
{
if(flag)
{ while(true)
{
synchronized(locka)
{
System.out.println("if lcoka ");
synchronized(lockb)
{
System.out.println("if lockb");
}
}
}
}
else()
{
synchronized(lockb)
{
System.out.println("else lockb");
synchronized(locka)
{
System.out.println("else locka");
}
}
}
}
}
class MyLock
{
Object locka=new Object();
Object lockb=new Object();
}
class
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}