------
Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
java基础知识-----多线程
引言
理解线程,先要理解进程。
1.进程
一个正在执行的程序,每一个执行程序都有一个执行顺序又叫控制单元。
2.线程
每一个控制单元就是线程。
3.多线程
jvm启动时,有一个java.exe的进程,该进程中至少有一个线程执行。
而该线程执行的代码在main方法中--主线 程。
jvm中除了有主线程还有 一个负责垃圾回收的线程。
像这种在一个程序中有多个线程执行的方式称为多线程。
4.多线程的意义在哪?
让线程产生同时运行的效果,提高运行效率。
5.cpu运行原理
1) 每一次运行效果不同源自其随机性。多个线程都在获取执行权,谁得到执行,谁就运行。
运行多久,cpu决定。
2)为什么会有同时执行效果?在同一时刻,只能有一个程序执行,之所以会有同时执行的效果是因为cpu在多个进 程之间做着快速的切换。
一、线程的创建
创建有两种方式
1. 继承Thread方式
1)定义类集成Thread -------class Demo extends Thread
2)复写run方法----------------public void run()
3)调用start方法开启线程-----demo d = new demo(); d.start();
注意只能用start方法,因为调用start会自动找run方法需要被执行的代码。进行线程开启。
而d.run()只是进行了run方法的调用。,不是开启线程。
//继承的方式创建线程
class Demo extends Thread
{
DemoName(String name)
{
super(name);
}
//复写方法
public void run()
{
// 此处放执行代码
for(int i=1;i<40;i++)
{
System.out.println(Thread.currentThread().getName()+"run"+i);
}
}
}
class Demo1
{
public static void main(String[] args)
{
//创建线程
Demo d1 = new Demo();
Demo d2 = new Demo();
//开启线程,调用start方法
d1.start();
d2.start();
}
}
2.实现Runnable接口
1)定义类实现Runnable接口-------class demo implements Runnable
2)覆盖Runnable中的run方法-------public void run()
3)创建线程对象---------------------Thread t = new Thread();
3) 创建Runnable接口子类对象-----demo d = new demo();
作为参数传递给Thread的构造函数,见上步。
4)调用start方法 -------------------t.satrt();
为什么将Runnabl子类对象作为参数进行传递?
因为因定义的run方法所属的对象时Runnable子类对象,所以要让线程去指定对象的run方法,
就必须明确该run方法所属的对象。
//用实现的方式创建线程
class Demo implements Runnable
{
private int ticket = 20;
Object obj = new Object();
//复写run方法
public void run()
{
synchronized(obj)//进行同步处理
{
while(true)
{
if(ticket>0)
{
//异常处理方式
try
{
Thread.sleep(10);
}
catch (Exception e)
{
System.out.println(e.toStrig());
}
System.out.println(Thread.currentThread().getName());
}
}
}
}
}
class Demo2
{
public static void main(String[] args)
{
Demo d = new Demo();
//创建线程
Thread d1 = new Thread(d);
Thread d2 = new Thread(d);
Thread d3 = new Thread(d);
//线程启动,调用start()方法
d1.start();
d2.start();
d3.start();
}
}
实现与继承的区别
实现:线程代码写在Runnab接口子类的run方法中。
继承:线程代码写在Thread子类run方法中
实现的好处
避免了单继承的局限性。建议使用。java只有单继承,没有多继承。
二、线程运行状态
run结束,消亡
|stop
被创建--------------------->运行<--(notify、notifyAll)------(wait、sleep)------>冻结
|
临时阻塞:具备执行资格,但没有执行权
1.线程运行中出现的安全问题
多条语句操作一个线程共享数据时,一个线程执行了一部分,还没执行完,另一个线程就参与执行,导致共享数据的错误。
2.对于安全问题java提供的解决方案
---同步代码块
synchronized(对象)
{
同步代码
}
3.解决的原理
保证同步中只有一个线程进行。加锁
好处
解决了多线程的安全问题。
弊端
多个线程要判断锁,耗费资源
-------------》加锁的前提:
1)有2个或2个以上的线程
2)多个线程要共用一个锁
4.扩展
1) 同步的表现形式
同步代码块
Object obj = new Object();
public void add(int n)
{
synchronized(obj)
{
同步代码;
}
}
2)同步函数
public synchronized void add(int n)
{
}
同步函数的锁是this,被对象调用,就有一个所属对象的引用
当为静态同步函数时,锁是所属类对应的字节码文件 Class对象
public static synchronized void add(int n)
{
synchronized(Ticjet.class)
{
同步执行代码
}
}
锁不是this,为什么?
静态函数中不能定义this,静态static进入内存时,没有本类对象,但必有该类对应的字节码文件.class
5.死锁
2个或者2个以上的进程在执行过程中,因为争夺资源而出现的一种相互等待的状态。
若无外力作用,则无法进行下去,则称系统处于死锁状态,或者系统产生了死锁。
当同步中嵌套同步时有可能出现死锁现象。
要会写一个死锁程序
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.locka)
{
System.out.println("----if locka");
synchronized(MyLock.lockb)
{
System.out.println("---if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)
{
System.out.println("---else lockb");
synchronized(MyLock.locka)
{
System.out.println("---else locka");
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
6.利用锁的单利设计模式
1)饿汉式
class Single
{
private finale Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
2)懒汉式-------加锁
class Single
{
private Single s = null;
private Single(){}
public static synchronized Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}
三、线程间通信
1.线程间通信
多个线程操作同一个资源,但操作动作不同。
当某些代码需要被同事执行时,用单独的线程进行封装。
/*
需求:买票程序
多个窗口同时买票
*/
class Ticket implements Runnable //实现Runnable接口好处:避免单继承的局限性
{
private int tick = 100;
Object obj = new Object();//Object为所有类的子类,无需定义
public void run()
{ //synchronized内放任意对象,
synchronized(obj)//同步代码块,{}内放需要被同步的代码
{ //哪些被同步,就哪些操作共享数据
while(true)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e ){}//此行代码可以显示出不安全数据
System.out.println(Thread.currentThread().getName()+"sale :"+tick--);
}
}
}
}
}
class maipiao2
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);//实现Runnable语句 new Thread(对象p);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/*
需求
银行有一个金库
有两个储户分别存300元,每次存100,存3次
目的:该程序是否有安全问题,如果有,如何解决?
如何找问题:
1.明确哪些代码是多线程运行代码
2.明确共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的
*/
class Bank
{
private int sum;
public void add(int n)
{
sum=sum+n;
System.out.println("sum="+sum);
}
}
class Cus implements Runnable
{
Bank b = new Bank();
public void run()
{
for(int i=0;i<3;i++)
{
b.add(100);
}
}
}
class tongbuhanshu
{
public static void main(String[] args)
{
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c);
t1.start();
t2.start();
t3.start();
}
}
2.等待唤醒机制
均使用在同步中,因为要对持有锁的线程操作,只有同步有锁。
操作方法定义在Object中
原因:
操作方法都在同步中,要使用这些方法必须要标示所属的同步锁。
而且只可被同一锁唤醒,锁又可以是任意对象,任意对象调用的方法
一定在Object方法中。
3.sleep与wait的区别
wait():等待,释放执行权,释放锁
sleep():释放执行权,不释放锁。
4.为什么定义notifyAll()
因为在唤醒对方线程时,如果使用notify,容易出现只唤醒本线程的情况,
导致程序中所有线程都等待。
5.jdk1.5后,停止线程的方法
1) 停止线程的一般方法
一般情况,有一个
run()
{
while(flag)
{
执行代码
}
}
只要在main方法,该线程执行一段时间,将flag进行标记为false,则线程就结束
2)特殊情况
线程处于冻结状态,不会读取到标记,就不会结束,
当没有指定的方式让冻结的线程恢复到正常运行状态当中时,
就需要对冻结进行强制解除,强制恢复到正常运行状态。
此时就可以读取标记,结束线程。------Thread -----interrupt
6.扩展
1)join方法
临时加入线程执行,当A执行到B的join时,A等待,B进行执行。
当B执行完后,A再进行执行。
2)守护线程
又叫后台线程
Thread.setDaemon(true);线程启动之前
启动后与前台线程共同抢cpu执行权,当所有前台都结束后,后台自动结束。
3)setPriority(Thread.MAX_PRIORITY);
设置线程优先级。
MAX_PRIORITY 最高级10级
MIN_PRIORITY 最低级 1级
NORM_PRIORITY 默认优先级5级
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------